llvm-mos llvm_mos https://llvm-mos.org/wiki/Welcome MediaWiki 1.43.3 first-letter Media Special Talk User User talk llvm-mos llvm-mos talk File File talk MediaWiki MediaWiki talk Template Template talk Help Help talk Category Category talk Module Module talk Welcome 0 1 1 2021-03-08T02:23:15Z MediaWiki default 2 wikitext text/x-wiki <strong>MediaWiki has been installed.</strong> Consult the [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide] for information on using the wiki software. == Getting started == * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list] * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ] * [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list] * [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language] * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki] 5702e4d5fd9173246331a889294caf01a3ad3706 2 1 2021-03-08T02:48:44Z Jbyrd 1 Trivial edit. wikitext text/x-wiki <strong>MediaWiki has been installed.</strong> Consult the [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide] for information. == Getting started == * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list] * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ] * [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list] * [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language] * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki] afa20e333c5a6c73f4ad94c39a26e47b6e8bd0cf 3 2 2021-03-08T02:51:58Z Jbyrd 1 /* Getting started */ wikitext text/x-wiki Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors. df9ea1918103450a6cb3f661dcadefba0f111b3d 6 3 2021-03-13T07:56:10Z Jbyrd 1 Jbyrd moved page [[Main Page]] to [[llvm-mos:Main Page]] wikitext text/x-wiki Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors. df9ea1918103450a6cb3f661dcadefba0f111b3d 9 6 2021-03-13T08:01:07Z Jbyrd 1 Jbyrd moved page [[llvm-mos:Main Page]] to [[llvm-mos:Welcome to llvm-mos]] wikitext text/x-wiki Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors. df9ea1918103450a6cb3f661dcadefba0f111b3d 11 9 2021-03-13T08:01:28Z Jbyrd 1 Jbyrd moved page [[llvm-mos:Welcome to llvm-mos]] to [[Welcome to llvm-mos]] wikitext text/x-wiki Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors. df9ea1918103450a6cb3f661dcadefba0f111b3d 13 11 2021-03-13T08:02:14Z Jbyrd 1 Jbyrd moved page [[Welcome to llvm-mos]] to [[Welcome]] wikitext text/x-wiki Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors. df9ea1918103450a6cb3f661dcadefba0f111b3d 16 13 2021-03-13T08:05:18Z Jbyrd 1 wikitext text/x-wiki Welcome to the llvm-mos project! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. 79d3dbfeaea91f7edfd31ebb4ab94b2b9e709502 18 16 2021-03-13T09:40:57Z Jbyrd 1 wikitext text/x-wiki Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. Although this project is in its infancy, it's still able to compile and link simple programs for several popular microcomputers of the 1980s. 85af5e4e71f51d616f3da312a2bb4fbcf25e5eee 19 18 2021-03-14T18:12:56Z Jbyrd 1 wikitext text/x-wiki Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s. 11cd53cbab46bd107faab6f97ac0df30fc573a45 21 19 2021-03-14T18:40:49Z Jbyrd 1 Add overview wikitext text/x-wiki Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s. * [[Overview]] 88fa22569f957946d919e0efaeab8f1dda35f3f9 23 21 2021-03-14T19:13:08Z Jbyrd 1 Add C64 image wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s. * [[Overview]] 37a01151fc2306510320f5871a6deb94bc867c6a 25 23 2021-03-14T19:14:56Z Jbyrd 1 Add VIC-20 image wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s. * [[Overview]] 95da4e0a1993486425d2e44b4c77db6d0cbf7b10 27 25 2021-03-14T19:15:54Z Jbyrd 1 Add Apple IIe wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors. This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets. Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s. * [[Overview]] 2e74272267b865f379677427a356d478b5b1f1f8 28 27 2021-03-14T19:27:08Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, it is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. We welcome your assistance and contributions to this project. 94cafb2082154daafc68a3371fcc0015a6195131 29 28 2021-03-14T19:27:43Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. We welcome your assistance and contributions to this project. b4da84cdef3ab1b98971c12aa023ac96f0f51b5d 30 29 2021-03-14T19:49:07Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile short programs for the 6502 series. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! a3bee2cb95e1e2cfba672a7c0ed210bb1012ec99 31 30 2021-03-14T19:50:08Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! 2bc2e3cfcbc5dbc7fc4650abf005722e257b6f72 32 31 2021-03-14T19:51:55Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur on [Slack, in the https://llvm-mos.slack.com/ llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! c36c2498c535a701a4a18bc63953c63b800af5b0 33 32 2021-03-14T19:52:34Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! 98925b908dd5c288bda25035c2b316f9fb4ab6bd 34 33 2021-03-14T19:53:04Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! 67a2e54e61b45b727a87aef297eab0a54e246d9c 35 34 2021-03-14T19:53:28Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] Welcome to the llvm-mos project! The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! 7997f30e2c712db3e0ec33e72de436535ec0b526 36 35 2021-03-14T19:56:22Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! 0eb36125387da5cdc3fb25936c17a02f3e919736 40 36 2021-03-14T20:09:29Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! == Key topics == * [[Assembler]] * 6610c05db116494d1b82a06733a4106209635003 MediaWiki:Sidebar 8 2 4 2021-03-08T19:23:17Z Jbyrd 1 Remove items from toolbar wikitext text/x-wiki * navigation ** mainpage|mainpage-description ** recentchanges-url|recentchanges * SEARCH * TOOLBOX * LANGUAGES cf3659e2214a1a0b03d425827bccf346d62ca332 MediaWiki:Mainpage 8 3 5 2021-03-13T07:55:26Z Jbyrd 1 Created page with "llvm-mos" wikitext text/x-wiki llvm-mos d584aef15e3d09e9e3b3a0dc9273a4a920b98d5c 8 5 2021-03-13T08:00:11Z Jbyrd 1 wikitext text/x-wiki Welcome to llvm-mos 8522d8dc1e6e7a0dd2d7fa610089754121d7a073 15 8 2021-03-13T08:02:45Z Jbyrd 1 wikitext text/x-wiki Welcome ca4f9dcf204e2037bfe5884867bead98bd9cbaf8 Main Page 0 4 7 2021-03-13T07:56:10Z Jbyrd 1 Jbyrd moved page [[Main Page]] to [[llvm-mos:Main Page]] wikitext text/x-wiki #REDIRECT [[llvm-mos:Main Page]] 789dd409bf14e59df1730b74d34d90dc38d078c2 llvm-mos:Main Page 4 5 10 2021-03-13T08:01:07Z Jbyrd 1 Jbyrd moved page [[llvm-mos:Main Page]] to [[llvm-mos:Welcome to llvm-mos]] wikitext text/x-wiki #REDIRECT [[llvm-mos:Welcome to llvm-mos]] 3efc41d79129eadb73d65ba1407405b3781eb47b llvm-mos:Welcome to llvm-mos 4 6 12 2021-03-13T08:01:28Z Jbyrd 1 Jbyrd moved page [[llvm-mos:Welcome to llvm-mos]] to [[Welcome to llvm-mos]] wikitext text/x-wiki #REDIRECT [[Welcome to llvm-mos]] 95cdae98026c30eb20acb9a95880eb3f6e2b0b44 Overview 0 9 20 2021-03-14T18:39:40Z Jbyrd 1 Created page with "The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [https://en.wikipe..." wikitext text/x-wiki The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [https://en.wikipedia.org/wiki/MOS_Technology MOS Technology] 65xx series of microprocessors and their clones. This will permit modern software and development methods to target common microcomputers of the 1980s, including but not limited to the [https://en.wikipedia.org/wiki/Commodore_64 Commodore 64], the [https://en.wikipedia.org/wiki/Apple_IIe Apple IIe], and the [https://en.wikipedia.org/wiki/Atari_8-bit_family Atari 8-bit family]. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. 8c894ec7af71271f84b3b5c6dadca06833f627cf File:Hello-c64.png 6 10 22 2021-03-14T19:04:21Z Jbyrd 1 wikitext text/x-wiki Initial bringup of assembler on C64 emulator 5a8f43802ce717562a5ba060b181430a487d2b6d File:Hello-vic20.png 6 11 24 2021-03-14T19:14:10Z Jbyrd 1 wikitext text/x-wiki Hello world of LLVM assembler targeting Commodore VIC-20 7f210be090e797335245af6a7dbf1395c670d738 File:Hello-apple2.png 6 12 26 2021-03-14T19:15:22Z Jbyrd 1 wikitext text/x-wiki Hello world targeting Apple IIe d07f6f8a4816d49f19695f126e7e024ac91803ee Assembler 0 13 37 2021-03-14T20:02:30Z Jbyrd 1 Created page with "The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer ma..." wikitext text/x-wiki The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos`) as a parameter to any tool. 47af71b392f8b9595f7f8026f259c55766d06040 38 37 2021-03-14T20:06:24Z Jbyrd 1 wikitext text/x-wiki The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. 6aa5f15b77237e411d07d14e5764bea642e829cf 39 38 2021-03-14T20:07:22Z Jbyrd 1 wikitext text/x-wiki The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. 7ed2ec136917853a29d72a6d4b86d9b2960acd55 ELF overview 0 14 41 2021-03-14T20:27:56Z Jbyrd 1 Created page with "Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to p..." wikitext text/x-wiki Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump basic information about MOS executables. a4284960ab7bab36eb904d7baa66506da9f5ed2c 42 41 2021-03-14T20:29:05Z Jbyrd 1 wikitext text/x-wiki Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump basic information about MOS executables. 4a3fa7eeabd2dc2d5d8b316144fb1b5415aa2fc5 43 42 2021-03-14T20:31:17Z Jbyrd 1 wikitext text/x-wiki Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that work on ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries. 6a440be632bbfe1cd637ff7c404dbb35c3e50d95 44 43 2021-03-14T20:32:00Z Jbyrd 1 wikitext text/x-wiki Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries. 49b259fcaecf8a393aea6ff03fa5692f49b6db8b 45 44 2021-03-14T20:34:13Z Jbyrd 1 Jbyrd moved page [[ELF]] to [[ELF overview]] without leaving a redirect wikitext text/x-wiki Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries. 49b259fcaecf8a393aea6ff03fa5692f49b6db8b ELF specification 0 15 46 2021-03-14T20:37:27Z Jbyrd 1 Created page with "This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of pro..." wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} | Name | Value | Description | | --- | --- | --- | | EM_MOS_6502 | 0x00000001 | MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [MCS6500 Microcomputer Family Programming Manual, Second Edition](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf). This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. | | EM_MOS_6502_BCD | 0x00000002 | MOS 6502 compatible [BCD (binary coded decimal) instruction handling](http://www.6502.org/tutorials/decimal_mode.html), including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [Ricoh 2A0x](https://en.wikipedia.org/wiki/Ricoh_2A03), have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. | EM_MOS_6502X | 0x00000004 | NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [6502/6510/8500/8502 Opcode matrix](http://www.oxyron.de/html/opcodes02.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. | | EM_MOS_65C02 | 0x00000008 | MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. | | EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. | | EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | | EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. | | EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | | EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt) [MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf) [Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf) 17cf049a560bad27aa6a4bf39df893dc8300549b 47 46 2021-03-14T20:40:41Z Jbyrd 1 wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [MCS6500 Microcomputer Family Programming Manual, Second Edition](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf). This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. | |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [BCD (binary coded decimal) instruction handling](http://www.6502.org/tutorials/decimal_mode.html), including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [Ricoh 2A0x](https://en.wikipedia.org/wiki/Ricoh_2A03), have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X | 0x00000004 | NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [6502/6510/8500/8502 Opcode matrix](http://www.oxyron.de/html/opcodes02.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. | |- |- | EM_MOS_65C02 | 0x00000008 | MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. | |- | EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. | |- | EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. | |- | EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |} # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt) [MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf) [Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf) 2a770872adb689d2c292df19a52a796e72c956e7 48 47 2021-03-14T20:41:26Z Jbyrd 1 wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [MCS6500 Microcomputer Family Programming Manual, Second Edition](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf). This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [BCD (binary coded decimal) instruction handling](http://www.6502.org/tutorials/decimal_mode.html), including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [Ricoh 2A0x](https://en.wikipedia.org/wiki/Ricoh_2A03), have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [6502/6510/8500/8502 Opcode matrix](http://www.oxyron.de/html/opcodes02.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |- | EM_MOS_65C02 | 0x00000008 | MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. | |- | EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. | |- | EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. | |- | EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |} # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt) [MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf) [Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf) 6117129570f3c8dc2f37d5a492e82b61b94793ab 49 48 2021-03-14T20:45:18Z Jbyrd 1 wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. | |- | EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. | |- | EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. | |- | EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |} # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt) [MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf) [Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf) aba37eb97c6fa281d45031710e82f3bb03558572 50 49 2021-03-14T21:11:00Z Jbyrd 1 wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. | |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. | |- | EM_MOS_W65816 || 0x0000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt) [MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf) [Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf) c01016396c3f6e52f3fdb1d834733a1aeff4a84f 51 50 2021-03-14T21:13:33Z Jbyrd 1 /* Machine type field */ wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x0000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt) [MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf) [Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf) 6b147d126e6d69e92ed15a7815046856a052e832 52 51 2021-03-14T21:15:11Z Jbyrd 1 wikitext text/x-wiki This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ Caption text |- ! Header text !! Header text !! Header text |- | Example || Example || Example |- | Example || Example || Example |- | Example || Example || Example |} {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} # Section flags Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. | Name | Value | Description | | --- | --- | --- | | SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. # Relocation types Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. # References [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]() [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] f90d1f5f4a0c9e1776eb82c6ee3d6a6593775afb ELF specification 0 15 53 52 2021-03-14T21:18:23Z Jbyrd 1 wikitext text/x-wiki - ELF specification for MOS-compatible processors - This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def). Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]() [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 3f5f773305c7fc387f3bc37fb75aa5572e3ebb06 54 53 2021-03-14T21:22:31Z Jbyrd 1 /* Relocation types */ wikitext text/x-wiki - ELF specification for MOS-compatible processors - This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def<nowiki> MOS.def].</nowiki> Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]() [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] f6eb697cd7930b2f6bf5769cbc304e7a7e4dde33 55 54 2021-03-14T21:22:49Z Jbyrd 1 wikitext text/x-wiki - ELF specification for MOS-compatible processors - This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def].</nowiki> Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]() [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] b4a344911c904c4ef329d2f6e20a1f4656f8a603 56 55 2021-03-14T21:24:06Z Jbyrd 1 wikitext text/x-wiki - ELF specification for MOS-compatible processors - This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def]. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]() [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 7eca7f2f28284e98570ae1976e04fbda5aefbeeb 57 56 2021-03-14T21:24:27Z Jbyrd 1 wikitext text/x-wiki - ELF specification for MOS-compatible processors - This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]() [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] f11c3a81745cbfa8f2b7b1151f2832f3497d9402 58 57 2021-03-14T21:24:56Z Jbyrd 1 wikitext text/x-wiki - ELF specification for MOS-compatible processors - This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] ab09e625ec0ea5328b47b2ee0ba7c4275478c926 59 58 2021-03-14T21:25:40Z Jbyrd 1 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} == Section flags == Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} == Relocation types == Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 8698e91e873771ecd9263482809695e3f06a452c 60 59 2021-03-14T21:26:44Z Jbyrd 1 /* Section flags */ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 927754240ca05b28154519af54e27d6d58ba45d3 ELF overview 0 14 61 45 2021-03-14T21:33:39Z Jbyrd 1 wikitext text/x-wiki Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries. There exists a more precise [[ELF specification]] for llvm-mos. 071e618f84a795698b50f6ddfb80e4739f9daeb2 Welcome 0 1 62 40 2021-03-14T21:34:43Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! == Key topics == * [[Assembler]] * Executable and linker format ** [[ELF overview]] ** [[ELF specification]] * 1538e8fa69b268f7da944f2b537aadf719d84b21 69 62 2021-03-15T08:47:26Z Jbyrd 1 Add Modifiers wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! == Key topics == * Assembly ** [[Assembler]] ** Executable and linker format ***[[ELF overview]] ***[[ELF specification]] ** [[Modifiers]] * C compiler [tbd] * 7b2cd6e0adf765742ee3b9b94e80b62ef10349ce 70 69 2021-03-15T08:55:51Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] ** Executable and linker format *** **[[Modifiers]] * C * Linking ** [[ELF overview]] **[[ELF specification]] * 1aac117ecd2f6420d33c90fad9abc73ea0df644e 71 70 2021-03-15T08:56:25Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C * Linking ** [[ELF overview]] **[[ELF specification]] * 73454e1a7ea615faec2abd74feb19f6ee618538e 73 71 2021-03-15T10:06:30Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C ** [[C calling convention]] * Linking ** [[ELF overview]] **[[ELF specification]] * 24fd2afe6697312e610bc18f104bcf35eb9b4a33 77 73 2021-03-15T10:22:40Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] **Zero page * C ** C compiler **[[C calling convention]] ** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[https://lld.llvm.org/ lld overview] * c57423bdfdd5beb58d9bb421bdfbe50ace3d6742 78 77 2021-03-15T10:25:48Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C ** [[C compiler]] ** [[C calling convention]] ** clang general documentation * Linking ** [[ELF overview]] **[[ELF specification]] * 3e3601218f7b1dcab3937c4bd80e673ca266d315 83 78 2021-03-15T10:36:02Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C ** [[C compiler]] ** [[C calling convention]] ** clang general documentation * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * ad21e477c68ba2830d02f608c583fa3384f4c0d1 84 83 2021-03-15T10:36:47Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * b5a4e0af0735b69a52686398902dd7bf49086ea8 85 84 2021-03-15T10:48:55Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * ed939f2ebbaaee67892d0a325190272d7feb847e 86 85 2021-03-15T10:54:03Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 90dc03c9867b796b64787530f479622734c3bbf4 87 86 2021-03-15T10:56:24Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * d9817393eea56eb3a93973c4ad0eb48603d998c7 97 87 2021-03-15T22:11:50Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 7a673d8dd69d787d84390774260f58cde9949be0 99 97 2021-04-18T23:40:08Z Jbyrd 1 Added link to debugging codegen wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [[Debugging code generation]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 278af6833c2bfce83ecfd9ab15f278e28eb2a9bb 102 99 2021-05-05T18:27:57Z 2603:8000:3F01:9052:A9A3:FE64:6BAE:2265 0 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [[Debugging code generation]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] * [[Emulator]] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 28615e000181a4f7a393c234bd143bc922e4be2a Modifiers 0 16 63 2021-03-15T02:04:56Z Jbyrd 1 Created page with "When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or..." wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA [the lowest byte in the 16-bit address of QSORT] However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well. In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the `QSORT` address is loaded into the accumulator. The following modifiers are available for your use: | Name | Description | | --- | --- | | mos16lo() or < | The lowest byte in the given 16-bit address. | | mos16hi() or > | The highest byte in the given 16-bit address. | | mos24bank() | The 8-bit bank portion of the given 24-bit address. | | mos24segment() | The segment 16-bit portion of the given 24-bit address. | | mos24segmentlo() | The lowest byte in the segment of the given 24-bit address. | | mos24segmenthi() | The highest byte in the segment of the given 24-bit address. | The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. 6ec33a443544a7d5ad1adc0dd516c7c70b991982 64 63 2021-03-15T02:10:21Z Jbyrd 1 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA [the lowest byte in the 16-bit address of QSORT] However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well. In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the `QSORT` address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Name !! Value !! Description |- | mos16lo() or < || The lowest byte in the given 16-bit address. |- | mos16hi() or > || The highest byte in the given 16-bit address. | mos24bank() | The 8-bit bank portion of the given 24-bit address. | | mos24segment() | The segment 16-bit portion of the given 24-bit address. | | mos24segmentlo() | The lowest byte in the segment of the given 24-bit address. | | mos24segmenthi() | The highest byte in the segment of the given 24-bit address. | The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. b75ab3e1543779874cde689c0921778e719d6c17 65 64 2021-03-15T02:12:55Z 104.189.8.152 0 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA [the lowest byte in the 16-bit address of QSORT] However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well. In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the `QSORT` address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | mos16lo() or < || The lowest byte in the given 16-bit address. |- | mos16hi() or > || The highest byte in the given 16-bit address. |- | mos24bank() | The 8-bit bank portion of the given 24-bit address. | | mos24segment() | The segment 16-bit portion of the given 24-bit address. | | mos24segmentlo() | The lowest byte in the segment of the given 24-bit address. | | mos24segmenthi() | The highest byte in the segment of the given 24-bit address. | The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. d5d06441218211102807498cb53c941e42ca63f4 66 65 2021-03-15T02:13:35Z 104.189.8.152 0 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA [the lowest byte in the 16-bit address of QSORT] However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well. In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the `QSORT` address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | mos16lo() or < || The lowest byte in the given 16-bit address. |- | mos16hi() or > || The highest byte in the given 16-bit address. |- | mos24bank() || The 8-bit bank portion of the given 24-bit address. |- | mos24segment() || The segment 16-bit portion of the given 24-bit address. |- | mos24segmentlo() || The lowest byte in the segment of the given 24-bit address. |- | mos24segmenthi() || The highest byte in the segment of the given 24-bit address. The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. bf24f1a91a0eb5a966996c89c119935b00e20953 67 66 2021-03-15T02:13:51Z 104.189.8.152 0 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA [the lowest byte in the 16-bit address of QSORT] However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well. In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the `QSORT` address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | mos16lo() or < || The lowest byte in the given 16-bit address. |- | mos16hi() or > || The highest byte in the given 16-bit address. |- | mos24bank() || The 8-bit bank portion of the given 24-bit address. |- | mos24segment() || The segment 16-bit portion of the given 24-bit address. |- | mos24segmentlo() || The lowest byte in the segment of the given 24-bit address. |- | mos24segmenthi() || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. 6b13699b7e99bb98e89180eecdfc1bea4d32572a 68 67 2021-03-15T08:46:22Z Jbyrd 1 Formatting cleanup wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. 0fe2ae61ef89719b495708167994ddc222830b93 C calling convention 0 17 72 2021-03-15T10:01:09Z Jbyrd 1 Created page with "The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning: * Aggregate types (structs, arrays, etc.) are..." wikitext text/x-wiki The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning: * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. It's not use having more than two, since at most 4 bytes of hard stack is allowed per frame, and spilling zero page registers to the soft stack isn't worthwhile. * - Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. * - For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] 95f70e6b4a59d3f1014590d9fedbeaee53d6f361 C compiler 0 18 74 2021-03-15T10:16:46Z Jbyrd 1 Created page with "A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag --target=mos to clang. Like all LLVM backends, the bulk..." wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag --target=mos to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle lowering to a degree unusual for a processor of MOS's size. fb7215a137bd9404233fe6a4a18b72e3b6a7becf 75 74 2021-03-15T10:17:56Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's tiny transistor count. 3126bd1454d457156a7a160bde10175d70f0c05c 76 75 2021-03-15T10:18:19Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. 6206d2e37b94abe855b3cb85733489aec996b117 88 76 2021-03-15T11:06:36Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called zero page, which could be accessed relatively quickly and cheaply. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', to diffentiate from LLVM's virtual registers. 7d214c0f95720c7a143333cd1b55be34cc27a8d1 89 88 2021-03-15T11:07:07Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', to diffentiate from LLVM's virtual registers. 9ab8701c0905d0e29d810791b3b7ddae272d5769 90 89 2021-03-15T11:08:23Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning. 73aad752c7f5b4c8f8aecfe3c0cf72c84875c571 91 90 2021-03-15T11:09:04Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning. 56486cbd47c23cde40e4cb56eafdf875cfcbf496 92 91 2021-03-15T11:09:18Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502, compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning. 3a3fb2fcad85a4a44665e4370794b77b18adddb1 93 92 2021-03-15T11:09:31Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning. e0d5c664d99875883283316f4412a02380f21440 94 93 2021-03-15T11:10:02Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning. dd8097310e3f8d8a80e69d78e9a2722d069f4422 95 94 2021-03-15T11:10:41Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', not to be confused with [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers]. 39f32533754647b908bc32f2ab4c10bac4ac0860 96 95 2021-03-15T11:14:03Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions. The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', which not to be confused with [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers]. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. However, the imaginary register locations are treated as symbols, which are not resolved to exact locations until link time. df6add2c88007745fca42e3bc4edbc8baaa5850e Assembler relaxation 0 19 79 2021-03-15T10:26:05Z Jbyrd 1 Created page with "The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hel..." wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as 0xb5, indicating that hello is an 8-bit (zero page) address. Alternately, it might be encoded as 0xbd, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the hello symbol, or (2) receive a hint as to which address space that hello should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: - the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or - the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or - the symbol is defined in a section marked with the special z flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section zp low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the adrlowmemory symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the low_short symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the lda instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. f8316d67cf5cac5914d1b12771cab8300b12a3aa 80 79 2021-03-15T10:27:55Z Jbyrd 1 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: <code>.section zplow_short:.byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x</code> A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the adrlowmemory symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the low_short symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the lda instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. 6bc03dc3a4f0077a7c1da0c3554c87220fd4e7d3 81 80 2021-03-15T10:28:47Z Jbyrd 1 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section zp low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x</code> A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the adrlowmemory symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the low_short symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the lda instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. 6ef7bcd48afe183ab921f3905557b4e665c235a3 82 81 2021-03-15T10:29:30Z Jbyrd 1 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section zp low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x</code> A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. c2871207f364a0e425504f4b177c94247df44e8a Debugging code generation 0 20 98 2021-04-18T23:39:04Z Jbyrd 1 Initial authoring wikitext text/x-wiki The llvm-mos code generator runs mostly on test cases written in MIR. These can be found in <code>llvm/test/CodeGen/MOS</code> . To test a particular section of codegen, you typically compile the code with clang, and then use llc to watch as the GlobalISel architecture lowers the code from generic MIR to MOS code. One way of doing this is by debugging program: llc -O2 -debug --debug-pass=Details -verify-machineinstrs -o %t %s Where %t is the assembly output file, and %s is the name of a LLVM bitcode file with an .ll extension. This command line runs all the GlobalISel passes, emitting copious debug information on every pass. b8f7f59d7afe604b0cc906cf617d106407521e2c 100 98 2021-04-21T20:49:41Z Jbyrd 1 wikitext text/x-wiki The llvm-mos code generator runs mostly on test cases written in MIR. These can be found in <code>llvm/test/CodeGen/MOS</code> . To test a particular section of codegen, you typically compile the code with clang, and then use llc to watch as the GlobalISel architecture lowers the code from generic MIR to MOS code. One way of doing this is by debugging program: llc -O2 -debug --debug-pass=Details -verify-machineinstrs -o %t %s Where %t is the assembly output file, and %s is the name of a LLVM bitcode file with an .ll extension. This command line runs all the GlobalISel passes, emitting copious debug information on every pass. A formula for creating C code to test codegen passes is as follows: clang --target=mos -emit-llvm -S %s -o %t.ll llc -debug --debug-pass=Details -mtriple=mos -verify-machineinstrs %t.ll -o %t.s 2c6e74e1b0a379d32d10e01b2ae22a8535e9e390 Emulator overview 0 21 101 2021-04-30T21:57:53Z Jbyrd 1 Created page with "As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well..." wikitext text/x-wiki As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well as test suite verification, without requiring actual hardware to be present. The intention of the emulator design is to permit multiple future iterations of emulation, without throwing away any existing functionality. Although a classic read-parse-execute style emulator would be sufficient for small tests, we'd want to be able to transpile generator code to the local machine's native instruction set, for maximum performance. We'd like to leave the door open to debugging with lldb or other tools, including reverse execution. And although all known LLVM targets have register sets and contiguous memory, we don't want to necessarily require any particular emulation to have these features. About 80% of the functionality required to build an emulator, is already present within LLVM for most targets. LLVM already includes a disassembler class, MCDisassembler, which is implemented for most platforms. It's able to reconstruct a series of MCInst objects given a raw instruction stream. So, what's needed is a way to take the "current" MCInst, and emulate the behavior of it. It's also natural to describe the behavior of each instruction, in the tablegen file that defines it. It allows similar instructions to be grouped and to share related code for related activities. Since LLVM's tablegen already has complete knowledge of all the instructions present for the target, it makes sense to explain the emulator behavior of these instructions to tablegen, and then let tablegen collate all these behaviors into an .inc file that can be included within a larger emulator. In keeping with the LLVM philosophy, MCEmulator is a set of building blocks for building emulators. It includes a command-line tool, llvm-emu, which demonstrates one way to combine those blocks into an emulator. 2942e7c8cb674e23ed075ec468c16d7598b71ca1 C calling convention 0 17 103 72 2021-05-05T18:33:21Z Jbyrd 1 wikitext text/x-wiki The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning: * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. It's not use having more than two, since at most 4 bytes of hard stack is allowed per frame, and spilling zero page registers to the soft stack isn't worthwhile. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. * For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] 87b53b75161578b442efda1b9da70b333c359dfa 104 103 2021-05-05T18:33:44Z Jbyrd 1 wikitext text/x-wiki The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning: * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. It's not use having more than two, since at most 4 bytes of hard stack is allowed per frame, and spilling zero page registers to the soft stack isn't worthwhile. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] 53180336e6da6fcc6b1540e60317eeda2a6b8b7f 110 104 2021-05-27T05:26:14Z Mysterymath 3 Provide full details of calling convention. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each imaginary (zero page) register, increasing. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers. If none are available, the bytes are split and passed as above. * If no registers are available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] dadcc0d7a17658abd0a80f5f18362091a076be3e Code generation overview 0 22 105 2021-05-15T20:48:37Z Jbyrd 1 Created page with "The LLVM GlobalISel architecture allows for adding multiple sequential passes, each of which does exactly one thing, for reducing [https://llvm.org/docs/GlobalISel/GMIR.html g..." wikitext text/x-wiki The LLVM GlobalISel architecture allows for adding multiple sequential passes, each of which does exactly one thing, for reducing [https://llvm.org/docs/GlobalISel/GMIR.html generic MIR] to 6502 code. Our current strategy is not to lower directly to 6502 code, but rather to lower MIR to a set of logical 6502-like opcodes. LLVM likes to operate on machines with plenty of registers, and instructions for which those registers can be changed. For example, instead of explaining the TYA, TAY, TXA, and TAX instructions to the mid-level pipeline, we create logical T_A and TA_ instructions, each of which accepts a register parameter which codegen can choose. These logical instructions are lowered much later in the pipeline to actual 6502 opcodes. This permits code generation to perceive the 6502 instruction set as a set of [opcode, register] pairs. c0dcc88de4f74865ba4e6817f6f9f99ad368e33b 112 105 2021-05-27T05:41:08Z Mysterymath 3 Move text from C compiler; that page should really talk more about the Clang side of things. wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from it's machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, which not to be confused with LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. 59c6e7b1cacef41e25b58d6621a3d71789cd4e34 114 112 2021-06-01T21:23:38Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from it's machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, which not to be confused with LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. 2e29c3ec5f6141e3710224ebf3ef718842103763 115 114 2021-06-01T21:24:05Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, which not to be confused with LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. bfe59cd60c9dc9eb47f05fb6a93d8e6b6688ff32 146 115 2021-06-02T07:58:54Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. f4b49189a1251859b6f5663aa57cf4d5db0df174 147 146 2021-06-02T07:59:17Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. 30b6f7c8a4612bc32f7a9676675727c56c8ec54d 148 147 2021-06-02T08:00:07Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. f33c3f13bfcbce5161c0336a713fd5e758f74559 Welcome 0 1 106 102 2021-05-15T20:49:06Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C compiler]] ** [[C calling convention]] ** [[Debugging code generation]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] **[[Code generation]] * [[Emulator]] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 58f9257a786f1077b0c708547a9333cb1e72dc24 107 106 2021-05-15T20:50:03Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 87f5a734cb21160456c5d2b976610fab187f8c12 108 107 2021-05-27T05:16:43Z Mysterymath 3 MInor fixes to wording on welcome page. wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * Linking **[[ELF overview]] **[[ELF specification]] **[[Zero page]] * 06d02d95a646da7f583601e387002e3ec6060d09 124 108 2021-06-01T22:10:54Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * Linking **[[ELF overview]] **[[ELF specification]] * 5686ff1549c741d5926c03df2546532701ecb639 125 124 2021-06-01T22:15:41Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * Linking **[[ELF overview]] **[[ELF specification]] * 2815c72d1ff615beac80b8c87cfe8647ec1d036a 126 125 2021-06-02T04:17:48Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] * 452fc5117f4525fed5f5f306407e8a55e005bacc 130 126 2021-06-02T04:24:39Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[SDK]] * 9b72ab1de286316c2b83db025982098b8e189d67 132 130 2021-06-02T04:27:17Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[SDK]] * 0f48e4e431b5f011e7efad9fd61442d1b81416ae 133 132 2021-06-02T04:52:15Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[SDK]] * c0065eca6d692681a41fc81df57840f5d0428cbf 134 133 2021-06-02T05:18:42Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[SDK]] * a30fdfeb22484760940c63b7045db06cf4491692 137 134 2021-06-02T05:39:19Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 6522d40213e4db6d7252bb8de62eba4306736f38 149 137 2021-06-02T09:44:36Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * ea237ad696e913809bb3b9574c7c08c785c7e4eb 151 149 2021-06-02T09:46:29Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 1d1c2807e6c17804ab68edfd2dd19bc34ec79a37 Modifiers 0 16 109 68 2021-05-27T05:18:40Z Mysterymath 3 Add mos8 modifier. wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. 21c85875f0ed34400ca41a56d704f6fac0612ad2 C compiler 0 18 111 96 2021-05-27T05:39:11Z Mysterymath 3 Filled out C compiler documentation with more detail. wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from it's machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', which not to be confused with [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers]. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. ae241fce0e491ea0fddb93846abf66eab81b2e9d 113 111 2021-05-27T05:54:37Z Mysterymath 3 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. The compiler is a freestanding implementation of the C99 standard, with a couple caveats. The biggest is that it isn't finished yet! Code generation is broken, both in terms of what compiles and in terms of whether or not the results actually work. Development is ongoing though, and test cases are moving from failed to passing at a considerable clip! Even for the initial release, there'll still be some caveats: * While this is a freestanding implementation, we'll also provide a few support libraries; mostly just those needed to get LLVM's end-to-end test suite passing. This includes printf, sprintf, and alloca. * While the float and double data types will be legal, any operations performed on them will emit calls to soft float routines that are ''not'' provided with the compiler. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. In the mean time, because the libcall names are fairly standard, it should be possible for users to develop their own implementation if they see fit. * The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines. b3196f59453c03c8de7d07f9d9fb6bcfc4383051 ELF specification 0 15 116 60 2021-06-01T21:27:10Z Jbyrd 1 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] df03a8a2ef05bb73b06fbf1f581364e8a8b1a7b8 117 116 2021-06-01T21:32:32Z Jbyrd 1 /* Machine type field */ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 493a98082f07100a8d8c91a69a26a8a7f9b9804c 118 117 2021-06-01T21:45:40Z Jbyrd 1 Relocation types filled out wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most signifcant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 4fc6b5307ab7caf7b5ff07cc878162a5dbefdc2a 119 118 2021-06-01T21:55:26Z Jbyrd 1 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative. Comments and improvements are solicited at johnwbyrd at gmail dot com. === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, shall be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although ELF generally represents 32-bit or 64-bit executables, there's no reason why ELF code ''must'' use that entire memory range. In particular, MOS-compatible processors only utilize 16, or at most 24, bits of address space. Unused bits in any 32-bit ELF address field should be set to zero. Setting them to any other value has undefined behavior. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most signifcant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 17bf8d73a26b8b1a40b7792700cd6f473f244b43 120 119 2021-06-01T21:58:47Z Jbyrd 1 /* ELF specification for MOS-compatible processors */ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, shall be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although ELF generally represents 32-bit or 64-bit executables, there's no reason why ELF code ''must'' use that entire memory range. In particular, MOS-compatible processors only utilize 16, or at most 24, bits of address space. Unused bits in any 32-bit ELF address field should be set to zero. Setting them to any other value has undefined behavior. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most signifcant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 6e6e823cac8d4bd415a57ffb01dc33566276bcb6 121 120 2021-06-01T21:59:13Z Jbyrd 1 /* Overview */ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, shall be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although ELF generally represents 32-bit or 64-bit executables, there's no reason why ELF code ''must'' use that entire memory range. In particular, MOS-compatible processors only utilize 16, or at most 24, bits of address space. Unused bits in any 32-bit ELF address field should be set to zero. Setting them to any other value has undefined behavior. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most signifcant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] af801145ac3b21c7b630fb39c8ce48b7ab9b926f 122 121 2021-06-01T22:08:59Z Jbyrd 1 /* ELF specification for MOS-compatible processors */ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most signifcant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] 7b7f5d93d717b5fcdabce1df4034d23bf0d4a3b9 Assembler relaxation 0 19 123 82 2021-06-01T22:10:16Z Jbyrd 1 Jbyrd moved page [[Zero page]] to [[Assembler relaxation]] without leaving a redirect: Assembler relaxation wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section zp low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x</code> A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. c2871207f364a0e425504f4b177c94247df44e8a Getting started 0 23 127 2021-06-02T04:21:57Z Jbyrd 1 Created page with "The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually bu..." wikitext text/x-wiki The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually build the new target. If you like, you can speed up the build by disabling the other targets: <code>-DLLVM_TARGETS_TO_BUILD="".</code> It's early days for Clang and the LLVM code generator, but the assembler and linker (LLD) are both [https://www.productplan.com/glossary/minimum-viable-product MVP] complete. Both use ELF as their object format; the target-specific ELF definitions were extended to accommodate the 6502. No libraries or platform support are included with the llvm-mos repository, to keep it a clean fork of LLVM. These are contained in the related [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk]. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can be used without the SDK. However, the various platform subtargets (e.g. apple2e, c64, etc) used by the compiler driver will require the SDK to be present. See the SDK for more information as it develops. === Getting the Source Code and Building === The LLVM Getting Started documentation may be out of date. The [https://clang.llvm.org/get_started.html Clang Getting Started page] might have more accurate information. This is an example work-flow and configuration to get and build the LLVM source: # Checkout LLVM (including related sub-projects like Clang): #* <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> #* Or, on windows, <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> # Configure and build LLVM and Clang: #* <code>cd llvm-project</code> #* <code>cmake -S llvm -B build -G <generator> -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" [options]</code> Some common build system generators are: #** <code>Ninja</code> --- for generating Ninja build files. Most llvm developers use Ninja. #** <code>Unix Makefiles</code> --- for generating make-compatible parallel makefiles. #** <code>Visual Studio</code> --- for generating Visual Studio projects and solutions. #** <code>Xcode</code> --- for generating Xcode projects. Some Common options: #** <code>-DLLVM_ENABLE_PROJECTS='...'</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use <code>-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"</code>. #** <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). #** <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is Debug. #** <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). #* <code>cmake --build build [-- [options] <target>]</code> or your build system specified above directly. #** The default target (i.e. <code>ninja</code> or <code>make</code>) will build all of LLVM. #** The <code>check-all</code> target (i.e. <code>ninja check-all</code>) will run the regression tests to ensure everything is in working order. #** CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. #** Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. #* For more information see [https://llvm.org/docs/CMake.html CMake] Consult the Getting Started with LLVM page for detailed information on configuring and compiling LLVM. You can visit Directory Layout to learn about the layout of the source code tree. a608d570c20397a48301515a4b4885a0fdfec95d 128 127 2021-06-02T04:22:28Z Jbyrd 1 wikitext text/x-wiki The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually build the new target. If you like, you can speed up the build by disabling the other targets: <code>-DLLVM_TARGETS_TO_BUILD=""</code> It's early days for Clang and the LLVM code generator, but the assembler and linker (LLD) are both [https://www.productplan.com/glossary/minimum-viable-product MVP] complete. Both use ELF as their object format; the target-specific ELF definitions were extended to accommodate the 6502. No libraries or platform support are included with the llvm-mos repository, to keep it a clean fork of LLVM. These are contained in the related [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk]. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can be used without the SDK. However, the various platform subtargets (e.g. apple2e, c64, etc) used by the compiler driver will require the SDK to be present. See the SDK for more information as it develops. === Getting the Source Code and Building === The LLVM Getting Started documentation may be out of date. The [https://clang.llvm.org/get_started.html Clang Getting Started page] might have more accurate information. This is an example work-flow and configuration to get and build the LLVM source: # Checkout LLVM (including related sub-projects like Clang): #* <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> #* Or, on windows, <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> # Configure and build LLVM and Clang: #* <code>cd llvm-project</code> #* <code>cmake -S llvm -B build -G <generator> -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" [options]</code> Some common build system generators are: #** <code>Ninja</code> --- for generating Ninja build files. Most llvm developers use Ninja. #** <code>Unix Makefiles</code> --- for generating make-compatible parallel makefiles. #** <code>Visual Studio</code> --- for generating Visual Studio projects and solutions. #** <code>Xcode</code> --- for generating Xcode projects. Some Common options: #** <code>-DLLVM_ENABLE_PROJECTS='...'</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use <code>-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"</code>. #** <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). #** <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is Debug. #** <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). #* <code>cmake --build build [-- [options] <target>]</code> or your build system specified above directly. #** The default target (i.e. <code>ninja</code> or <code>make</code>) will build all of LLVM. #** The <code>check-all</code> target (i.e. <code>ninja check-all</code>) will run the regression tests to ensure everything is in working order. #** CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. #** Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. #* For more information see [https://llvm.org/docs/CMake.html CMake] Consult the Getting Started with LLVM page for detailed information on configuring and compiling LLVM. You can visit Directory Layout to learn about the layout of the source code tree. 20eaa6e2805951d71e89000b71e5e74669155f96 129 128 2021-06-02T04:23:31Z Jbyrd 1 wikitext text/x-wiki The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually build the new target. If you like, you can speed up the build by disabling the other targets: <code>-DLLVM_TARGETS_TO_BUILD=""</code> It's early days for Clang and the LLVM code generator, but the [[assembler]] and [[linker]] (LLD) are both [https://www.productplan.com/glossary/minimum-viable-product MVP] complete. Both use [[ELF overview|ELF]] as their object format; the target-specific [[ELF definitions]] were extended to accommodate the 6502. No libraries or platform support are included with the llvm-mos repository, to keep it a clean fork of LLVM. These are contained in the related [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk]. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can be used without the SDK. However, the various platform subtargets (e.g. apple2e, c64, etc) used by the compiler driver will require the SDK to be present. See the SDK for more information as it develops. === Getting the Source Code and Building === The LLVM Getting Started documentation may be out of date. The [https://clang.llvm.org/get_started.html Clang Getting Started page] might have more accurate information. This is an example work-flow and configuration to get and build the LLVM source: # Checkout LLVM (including related sub-projects like Clang): #* <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> #* Or, on windows, <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> # Configure and build LLVM and Clang: #* <code>cd llvm-project</code> #* <code>cmake -S llvm -B build -G <generator> -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" [options]</code> Some common build system generators are: #** <code>Ninja</code> --- for generating Ninja build files. Most llvm developers use Ninja. #** <code>Unix Makefiles</code> --- for generating make-compatible parallel makefiles. #** <code>Visual Studio</code> --- for generating Visual Studio projects and solutions. #** <code>Xcode</code> --- for generating Xcode projects. Some Common options: #** <code>-DLLVM_ENABLE_PROJECTS='...'</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use <code>-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"</code>. #** <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). #** <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is Debug. #** <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). #* <code>cmake --build build [-- [options] <target>]</code> or your build system specified above directly. #** The default target (i.e. <code>ninja</code> or <code>make</code>) will build all of LLVM. #** The <code>check-all</code> target (i.e. <code>ninja check-all</code>) will run the regression tests to ensure everything is in working order. #** CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. #** Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. #* For more information see [https://llvm.org/docs/CMake.html CMake] Consult the Getting Started with LLVM page for detailed information on configuring and compiling LLVM. You can visit Directory Layout to learn about the layout of the source code tree. ac2de84c6ca0a92cf256bf4f786723c79e69a28f SDK overview 0 24 131 2021-06-02T04:26:05Z Jbyrd 1 Created page with "A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises..." wikitext text/x-wiki A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises all the platform-specific libraries, includes, and examples that can be helpful using llvm-mos with a specific target. 56ef83a903abddad85663e53dfa87f77e0080055 Current status 0 25 135 2021-06-02T05:28:51Z Jbyrd 1 Created page with "'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running..." wikitext text/x-wiki '''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == C compiler == An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Currently, it also has notable codegen bugs that prevents it from being used for any but the smallest programs, but progress is ongoing. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. 319340240bf06be171582c310135511a7cd5ee47 136 135 2021-06-02T05:30:24Z Jbyrd 1 wikitext text/x-wiki == Overview == '''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == C compiler == An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Currently, it also has notable codegen bugs that prevents it from being used for any but the smallest programs, but progress is ongoing. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. 7e8339f21beeedb2e6017adc60d878f4124b87a3 Rationale 0 26 138 2021-06-02T05:55:33Z Jbyrd 1 Created page with "== Why yet another 6502 C compiler? == After all, there's cc65, and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient an..." wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's cc65, and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. 5088d41f68daacb8b988ce9ca10a577ffc8f3c73 139 138 2021-06-02T06:04:42Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's cc65, and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. 8768c18c918b6dd8edb2956ea7b4479779084247 140 139 2021-06-02T06:06:03Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. a697458d207811c40e31678976e85993dd5691f3 141 140 2021-06-02T06:06:32Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. 0d778062ab3523a536062a6df96bde9dee958738 142 141 2021-06-02T06:11:53Z Jbyrd 1 /* Performance */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. ae8b52a985fb9f737a8e1a54319509049721e129 143 142 2021-06-02T07:44:31Z Jbyrd 1 /* Features */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that llvm-mos can support Rust as a source language. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. a626ae51055c12177889bd6b68a026f29694fdb7 144 143 2021-06-02T07:45:24Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that llvm-mos can support Rust as a source language. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. 8ea9b81b171f658b4964b68a412e1d8d773c3481 145 144 2021-06-02T07:51:04Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. 7ef85ef27c7df2ad3e135cbcc08a1378d3142adb Building llvm-mos on Windows 0 27 150 2021-06-02T09:45:48Z Jbyrd 1 Created page with "Generally speaking, builds of LLVM on Windows are finicky and opinionated, because LLVM is finicky and opinionated to build on Windows. I use a stable release of LLVM to compi..." wikitext text/x-wiki Generally speaking, builds of LLVM on Windows are finicky and opinionated, because LLVM is finicky and opinionated to build on Windows. I use a stable release of LLVM to compile LLVM. When checking out from git, ''it's important to make sure that core.autocrlf is set to False''. Windows installations strongly encourage you to set this to true, but if you do, then LLVM acceptance tests will fail. To set up a Windows development and testing machine, I install Chocolatey first, at an administrator PowerShell prompt: <code>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<nowiki>https://chocolatey.org/install.ps1'</nowiki>))</code> Then, I run the following commands to install all prerequisites: <code>choco install git.install --params "/NoAutoCrlf" choco install cmake python llvm vscode ninja visualstudio2019community visualstudio2019-workload-nativedesktop</code> The LLVM documentation suggests that you need the GnuWin32 utilities, but I don't think that's necessary; you can simply use the ones that the Windows version of git installs at <code>C:/Program Files/git/usr/bin</code> . You can point the LLVM_LIT_TOOLS_DIR build variable at that directory, and llvm-lit will use all the tools in that directory for building the check-all target. As of this writing, my Windows build batch file looks like this: <code>set "WORKSPACE_FORWARD_SLASH=%WORKSPACE:\=/%" set LLVM_BIN=C:/Progra~1/LLVM/bin cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=%WORKSPACE_FORWARD_SLASH%/build/install -DLLVM_LIT_TOOLS_DIR=C:/Progra~2/GnuWin32/bin -DCMAKE_AR=%LLVM_BIN%/llvm-ar -DCMAKE_RANLIB=%LLVM_BIN%/llvm-ranlib -DCMAKE_C_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_CXX_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_LINKER=%LLVM_BIN%/lld-link.exe -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all -DLLVM_LIT_TOOLS_DIR="C:/Program Files/git/usr/bin" cmake --build . --config Release --target check-all cmake --build . --config Release --target install</code> cbb7957107d0e67660ecc64f49c9d461d7a17480 Building llvm-mos on MacOS 0 28 152 2021-06-02T09:46:39Z Jbyrd 1 Created page with "The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed: <code>cmake ../../llvm -G Ninja -DLLVM_EXPERIMEN..." wikitext text/x-wiki The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed: <code>cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=${WORKSPACE}/build/stage2/install -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all cmake --build . --config Release --target check-all cmake --build . --config Release --target install</code> 94a9658278f686ce6cd5fb9568944019ff5a4a8d Building llvm-mos on Windows 0 27 153 150 2021-06-02T09:48:30Z Jbyrd 1 wikitext text/x-wiki Generally speaking, builds of llvm-mos on Windows are finicky and opinionated, because LLVM itself is finicky and opinionated to build on Windows. I use a stable release of LLVM to compile LLVM. When checking out from git, ''it's important to make sure that core.autocrlf is set to False''. Windows installations strongly encourage you to set this to true, but if you do, then LLVM acceptance tests will fail. To set up a Windows development and testing machine, I install Chocolatey first, at an administrator PowerShell prompt: <code>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<nowiki>https://chocolatey.org/install.ps1'</nowiki>))</code> Then, I run the following commands to install all prerequisites: <code>choco install git.install --params "/NoAutoCrlf" choco install cmake python llvm vscode ninja visualstudio2019community visualstudio2019-workload-nativedesktop</code> The LLVM documentation suggests that you need the GnuWin32 utilities, but I don't think that's necessary; you can simply use the ones that the Windows version of git installs at <code>C:/Program Files/git/usr/bin</code> . You can point the LLVM_LIT_TOOLS_DIR build variable at that directory, and llvm-lit will use all the tools in that directory for building the check-all target. As of this writing, my Windows build batch file looks like this: <code>set "WORKSPACE_FORWARD_SLASH=%WORKSPACE:\=/%" set LLVM_BIN=C:/Progra~1/LLVM/bin cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=%WORKSPACE_FORWARD_SLASH%/build/install -DCMAKE_AR=%LLVM_BIN%/llvm-ar -DCMAKE_RANLIB=%LLVM_BIN%/llvm-ranlib -DCMAKE_C_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_CXX_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_LINKER=%LLVM_BIN%/lld-link.exe -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all -DLLVM_LIT_TOOLS_DIR="C:/Program Files/git/usr/bin" cmake --build . --config Release --target check-all cmake --build . --config Release --target install</code> 00d44c86153e39e9ce73648ee16a52916da0d9ca 182 153 2021-06-03T16:32:01Z Jbyrd 1 wikitext text/x-wiki Generally speaking, builds of llvm-mos on Windows are finicky and opinionated, because LLVM itself is finicky and opinionated to build on Windows. I use a stable release of LLVM to compile LLVM. When checking out from git, ''it's important to make sure that core.autocrlf is set to False''. Windows installations strongly encourage you to set this to true, but if you do, then LLVM acceptance tests will fail. To set up a Windows development and testing machine, I install Chocolatey first, at an administrator PowerShell prompt: <code>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<nowiki>https://chocolatey.org/install.ps1'</nowiki>))</code> Then, I run the following commands to install all prerequisites: <code>choco install git.install --params "/NoAutoCrlf" choco install cmake python llvm vscode ninja visualstudio2019community visualstudio2019-workload-nativedesktop</code> The LLVM documentation suggests that you need the GnuWin32 utilities, but I don't think that's necessary; you can simply use the ones that the Windows version of git installs at <code>C:/Program Files/git/usr/bin</code> . You can point the LLVM_LIT_TOOLS_DIR build variable at that directory, and llvm-lit will use all the tools in that directory for building the check-all target. As of this writing, my Windows build batch file looks like this: <code>set "WORKSPACE_FORWARD_SLASH=%WORKSPACE:\=/%" set LLVM_BIN=C:/Progra~1/LLVM/bin cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=%WORKSPACE_FORWARD_SLASH%/build/install -DCMAKE_AR=%LLVM_BIN%/llvm-ar -DCMAKE_RANLIB=%LLVM_BIN%/llvm-ranlib -DCMAKE_C_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_CXX_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_LINKER=%LLVM_BIN%/lld-link.exe -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all -DLLVM_LIT_TOOLS_DIR="C:/Program Files/git/usr/bin" cmake --build . --config Release --target check-all cmake --build . --config Release --target install</code> [[Category:Building]] e1bc322eed324bc4cd0ae2fbe57a13f41ddf6e35 Welcome 0 1 154 151 2021-06-02T09:53:52Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Root]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 477dddf61d2846e5538e90b9130c8c8cfd711744 158 154 2021-06-02T09:59:59Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Root]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === <categorytree mode="pages">Root</categorytree> * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 671341ce6884dc2ddfece3896f40af0d5f9a00e6 159 158 2021-06-02T10:00:15Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Root]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === <categorytree mode="pages">Assembly</categorytree> * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * c125f426488b44d653c61e842dc34f18e7c1e75b 160 159 2021-06-02T10:01:30Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Root]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === <categorytree>Assembly</categorytree> * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * cc08b8f8298a3c8075721bf2660549d735c6392e 161 160 2021-06-02T10:01:56Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Root]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 477dddf61d2846e5538e90b9130c8c8cfd711744 164 161 2021-06-02T10:06:38Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Root]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! <CategoryTree>Main</CategoryTree> === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * b2629a2db43b270054692a37a8c56c729504eb32 165 164 2021-06-02T10:07:21Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! <CategoryTree>Main</CategoryTree> === Key topics === * Assembly **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * bdbf483d7f08e0f9d31cdb6ddb95568ef8b19fb8 168 165 2021-06-02T10:11:18Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! <CategoryTree>Main</CategoryTree> === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * fda1fc0974b75b3390835732b25cfac7b8592bc8 170 168 2021-06-02T10:14:50Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 9b66becdbaeebe4b0909440720e55a46fab6b11d 171 170 2021-06-02T10:18:36Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! <CategoryTree mode="pages" depth="3" hideroot="on" font-size="150%">Main</CategoryTree> === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] * 44123b90783c6a84ecc1af4d891d50bc06080364 172 171 2021-06-02T10:22:43Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *Building llvm-mos **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 695fa4f69467836108fa5b1cee7fbc9d8ee07ccd 179 172 2021-06-03T16:30:33Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * C **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> e034fc3580bd4721204be9b1773e7c3fefecda09 183 179 2021-06-03T16:32:54Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[Code generation]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 469be1eebda4cac4d43bd58191f2d1cb337666af 188 183 2021-06-03T16:36:57Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *Code generation **[[Code generation overview]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 0461d91a2ac00f828c62e8e5f809652fee93abef 189 188 2021-06-03T16:37:25Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] *[[Current status]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 5e778d979dc76e64b6e8e4c4e7343135d89c2bbc 193 189 2021-06-03T16:40:59Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[Emulator]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 204e57bd268162facf780ee732a31b85cd4879aa 197 193 2021-06-03T16:42:26Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** Emulator overview * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> a9efd2a2d62be6241a16939f1170f7c049e82d4a 199 197 2021-06-03T16:43:04Z Jbyrd 1 /* Key topics */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 2b9e23b1d931a4a6d2d5cfee350996eef6cd0c4d 204 199 2021-06-03T16:47:36Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> [[Category:Linking]] 906847bd83b5da2361ad1f49f0b34125b383eeab 205 204 2021-06-03T16:47:57Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * Linking **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 2b9e23b1d931a4a6d2d5cfee350996eef6cd0c4d Assembler 0 13 155 39 2021-06-02T09:55:17Z Jbyrd 1 wikitext text/x-wiki [[Category:Assembler]] The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. e0e576f9bbc63b1804a7f17db9da650608b39fc9 156 155 2021-06-02T09:55:30Z Jbyrd 1 wikitext text/x-wiki [[Category:Assembler]] The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. 40ab4639e00ce8e4d400ab4173a089724b8557a6 162 156 2021-06-02T10:04:27Z Jbyrd 1 wikitext text/x-wiki The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. [[Category:Assembly]] 2b6b2bb2e90bbb26d46014b1ae18ebb969cfae4e Category:Assembly 14 30 163 2021-06-02T10:05:14Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 Category:Main 14 31 166 2021-06-02T10:08:35Z Jbyrd 1 Created blank page wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Assembler relaxation 0 19 167 123 2021-06-02T10:10:45Z Jbyrd 1 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section zp low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x</code> A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] 2e2cb88eca46365156fa8d6d833091cebed532c3 Modifiers 0 16 169 109 2021-06-02T10:11:50Z Jbyrd 1 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. [[Category:Assembly]] f4672e19a864b314abf909548374bb43af9896b4 Rationale 0 26 173 145 2021-06-02T10:31:31Z 104.189.8.152 0 /* Performance */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. And, for those programs that do require a soft stack, llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. b8d79941520c20081844b8d43b0522510239b859 C compiler 0 18 174 113 2021-06-02T19:36:42Z 71.198.117.145 0 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. The compiler is a freestanding implementation of the C99 standard, with a couple caveats. The biggest is that it isn't finished yet! Code generation is broken, both in terms of what compiles and in terms of whether or not the results actually work. Development is ongoing though, and test cases are moving from failed to passing at a considerable clip! Even for the initial release, there'll still be some caveats: * While this is a freestanding implementation, we'll also provide a few support libraries; mostly just those needed to get LLVM's end-to-end test suite passing. This includes printf, sprintf, and alloca. * No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. * The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines. f2a29da6f9499d726337276163a06d4f77943cce 186 174 2021-06-03T16:34:47Z Jbyrd 1 wikitext text/x-wiki A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. The compiler is a freestanding implementation of the C99 standard, with a couple caveats. The biggest is that it isn't finished yet! Code generation is broken, both in terms of what compiles and in terms of whether or not the results actually work. Development is ongoing though, and test cases are moving from failed to passing at a considerable clip! Even for the initial release, there'll still be some caveats: * While this is a freestanding implementation, we'll also provide a few support libraries; mostly just those needed to get LLVM's end-to-end test suite passing. This includes printf, sprintf, and alloca. * No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. * The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines. [[Category:C]] 066607b000a7cfcdbfe2faca465a643159c274ab Code generation overview 0 22 175 148 2021-06-02T19:40:15Z 71.198.117.145 0 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit constant and 8-bit offset. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. d7b8c8471961a386cddee90176cf509c748e545b 176 175 2021-06-02T19:41:31Z 71.198.117.145 0 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit constant and 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. ef8883effcd734f6a2cafe2168df12577966159d 177 176 2021-06-02T19:42:20Z 71.198.117.145 0 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit constant and 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. 8335bf29df7f7dfdf30fa57b229dc852cd872af7 178 177 2021-06-02T19:43:00Z 71.198.117.145 0 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. c64768a0e0ff876ac705875ab0fb78fe148bfbe5 187 178 2021-06-03T16:36:17Z Jbyrd 1 Jbyrd moved page [[Code generation]] to [[Code generation overview]] without leaving a redirect wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. c64768a0e0ff876ac705875ab0fb78fe148bfbe5 191 187 2021-06-03T16:38:06Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. [[Category:Code generation]] fd4db28d07cad6d1527a4b7cc9c21f5a46c53c7b Category:Building 14 32 180 2021-06-03T16:30:54Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 Building llvm-mos on MacOS 0 28 181 152 2021-06-03T16:31:36Z Jbyrd 1 wikitext text/x-wiki The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed: <code>cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=${WORKSPACE}/build/stage2/install -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all cmake --build . --config Release --target check-all cmake --build . --config Release --target install</code> [[Category:Building]] 18f98c093de0e432e24b65e5fa605a71d63c875a Category:C 14 33 184 2021-06-03T16:33:22Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 C calling convention 0 17 185 110 2021-06-03T16:33:44Z Jbyrd 1 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each imaginary (zero page) register, increasing. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers. If none are available, the bytes are split and passed as above. * If no registers are available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 0256ea557f1b753a17c64b1be7f43c5fbbde1cf8 Category:Code generation 14 34 190 2021-06-03T16:37:40Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 Debugging code generation 0 20 192 100 2021-06-03T16:38:26Z Jbyrd 1 wikitext text/x-wiki The llvm-mos code generator runs mostly on test cases written in MIR. These can be found in <code>llvm/test/CodeGen/MOS</code> . To test a particular section of codegen, you typically compile the code with clang, and then use llc to watch as the GlobalISel architecture lowers the code from generic MIR to MOS code. One way of doing this is by debugging program: llc -O2 -debug --debug-pass=Details -verify-machineinstrs -o %t %s Where %t is the assembly output file, and %s is the name of a LLVM bitcode file with an .ll extension. This command line runs all the GlobalISel passes, emitting copious debug information on every pass. A formula for creating C code to test codegen passes is as follows: clang --target=mos -emit-llvm -S %s -o %t.ll llc -debug --debug-pass=Details -mtriple=mos -verify-machineinstrs %t.ll -o %t.s [[Category:Code generation]] e04fd7a9b3198118f2075f7ff8b099013515472f Emulator overview 0 21 194 101 2021-06-03T16:41:12Z Jbyrd 1 Jbyrd moved page [[Emulator]] to [[Emulator overview]] wikitext text/x-wiki As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well as test suite verification, without requiring actual hardware to be present. The intention of the emulator design is to permit multiple future iterations of emulation, without throwing away any existing functionality. Although a classic read-parse-execute style emulator would be sufficient for small tests, we'd want to be able to transpile generator code to the local machine's native instruction set, for maximum performance. We'd like to leave the door open to debugging with lldb or other tools, including reverse execution. And although all known LLVM targets have register sets and contiguous memory, we don't want to necessarily require any particular emulation to have these features. About 80% of the functionality required to build an emulator, is already present within LLVM for most targets. LLVM already includes a disassembler class, MCDisassembler, which is implemented for most platforms. It's able to reconstruct a series of MCInst objects given a raw instruction stream. So, what's needed is a way to take the "current" MCInst, and emulate the behavior of it. It's also natural to describe the behavior of each instruction, in the tablegen file that defines it. It allows similar instructions to be grouped and to share related code for related activities. Since LLVM's tablegen already has complete knowledge of all the instructions present for the target, it makes sense to explain the emulator behavior of these instructions to tablegen, and then let tablegen collate all these behaviors into an .inc file that can be included within a larger emulator. In keeping with the LLVM philosophy, MCEmulator is a set of building blocks for building emulators. It includes a command-line tool, llvm-emu, which demonstrates one way to combine those blocks into an emulator. 2942e7c8cb674e23ed075ec468c16d7598b71ca1 200 194 2021-06-03T16:43:51Z Jbyrd 1 wikitext text/x-wiki As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well as test suite verification, without requiring actual hardware to be present. The intention of the emulator design is to permit multiple future iterations of emulation, without throwing away any existing functionality. Although a classic read-parse-execute style emulator would be sufficient for small tests, we'd want to be able to transpile generator code to the local machine's native instruction set, for maximum performance. We'd like to leave the door open to debugging with lldb or other tools, including reverse execution. And although all known LLVM targets have register sets and contiguous memory, we don't want to necessarily require any particular emulation to have these features. About 80% of the functionality required to build an emulator, is already present within LLVM for most targets. LLVM already includes a disassembler class, MCDisassembler, which is implemented for most platforms. It's able to reconstruct a series of MCInst objects given a raw instruction stream. So, what's needed is a way to take the "current" MCInst, and emulate the behavior of it. It's also natural to describe the behavior of each instruction, in the tablegen file that defines it. It allows similar instructions to be grouped and to share related code for related activities. Since LLVM's tablegen already has complete knowledge of all the instructions present for the target, it makes sense to explain the emulator behavior of these instructions to tablegen, and then let tablegen collate all these behaviors into an .inc file that can be included within a larger emulator. In keeping with the LLVM philosophy, MCEmulator is a set of building blocks for building emulators. It includes a command-line tool, llvm-emu, which demonstrates one way to combine those blocks into an emulator. [[Category:Emulator]] 48a63f41f6eee82544383c051795749a800156ae Category:Emulator 14 36 198 2021-06-03T16:42:42Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 Getting started 0 23 201 129 2021-06-03T16:45:37Z Jbyrd 1 wikitext text/x-wiki WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS. To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk. For more information about this project, please see llvm-mos.org. For information about the current status of this project, please see [[Current status|Current status.]] To learn why this project exists, please see [[Rationale]]. = Getting started = == Download the LLVM-MOS tools == If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform: * MacOS * Linux * Windows These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality. == Or, build the LLVM-MOS tools == However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform. Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM. Once you meet those requirements, you may use the following formula within your build environment: === Clone the LLVM-MOS repository === On Linux and MacOS: <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> On Windows: <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> If you fail to use the --config flag as above, then verification tests will fail on Windows. === Configure the LLVM-MOS project === <code>cd llvm-mos cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code> This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment. Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake. * <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command. * <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. * <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). * <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file. * <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). === Build the LLVM-MOS project === <code>cmake --build build [-- [options] <target>]</code> The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution. CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. = Help us out = We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes. All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules. If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality. You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests. Additionally, the current state of our documentation at <nowiki>https://llvm-mos.org</nowiki> can always use improvements and clarifications. fb98105011fe7b3159611016d00cf1e7da6de775 202 201 2021-06-03T16:46:11Z Jbyrd 1 wikitext text/x-wiki WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS. To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk. For more information about this project, please see llvm-mos.org. For information about the current status of this project, please see [[Current status|Current status.]] To learn why this project exists, please see [[Rationale]]. = Getting started = == Download the LLVM-MOS tools == If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform: * MacOS * Linux * Windows These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality. == Or, build the LLVM-MOS tools == However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform. Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM. Once you meet those requirements, you may use the following formula within your build environment: === Clone the LLVM-MOS repository === On Linux and MacOS: <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> On Windows: <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> If you fail to use the --config flag as above, then verification tests will fail on Windows. === Configure the LLVM-MOS project === <code>cd llvm-mos cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code> This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment. Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake. * <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command. * <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. * <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). * <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file. * <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). === Build the LLVM-MOS project === <code>cmake --build build [-- [options] <target>]</code> The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution. CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. = Help us out = We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes. All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules. If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality. You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests. Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications. 7bfc8ccbdb2a6e5f3986c5e3ef7958c54e66bf82 203 202 2021-06-03T16:46:54Z Jbyrd 1 wikitext text/x-wiki WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS. To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk. For more information about this project, please see llvm-mos.org. For information about the current status of this project, please see [[Current status|Current status.]] To learn why this project exists, please see [[Rationale]]. = Getting started = == Download the LLVM-MOS tools == If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform: * MacOS * Linux * Windows These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality. == Or, build the LLVM-MOS tools == However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform. Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM. Once you meet those requirements, you may use the following formula within your build environment: === Clone the LLVM-MOS repository === On Linux and MacOS: <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> On Windows: <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> If you fail to use the --config flag as above, then verification tests will fail on Windows. === Configure the LLVM-MOS project === <code>cd llvm-mos cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code> This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment. Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake. * <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command. * <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. * <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). * <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file. * <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). === Build the LLVM-MOS project === <code>cmake --build build [-- [options] <target>]</code> The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution. CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. = Help us out = We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes. All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules. If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality. You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests. Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications. [[Category:Main]] 7f6a87cec9708ca87a2c082ed2cddaa5e778b6f8 Welcome 0 1 206 205 2021-06-03T16:48:20Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * [[:Category:Linking|Linking]] **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> 72f14374a3d50380ae388eabf96d72f427161095 210 206 2021-06-03T16:55:17Z Jbyrd 1 /* Category tree (experimental) */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * [[:Category:Linking|Linking]] **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (experimental) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === All pages 14 === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} === Categories === {{Special:Categories}} 026e26f9bd6512edf88acd22086ff33d5ab89903 211 210 2021-06-03T16:58:30Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * [[:Category:Linking|Linking]] **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (requires Ajax and Javascript) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 09543ce43fefe79b3f9506adaa2cec40034d7b1f 217 211 2021-06-03T17:06:13Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Key topics === * [[:Category:Assembly|Assembly]] **[[Assembler]] **[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]] *** **[[Modifiers]] *[[:Category:Building|Building]] **[[Building llvm-mos on MacOS|On MacOS]] **[[Building llvm-mos on Windows|On Windows]] * [[:Category:C|C]] **[[C calling convention]] **[[C compiler]] **[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference] *[[:Category:Code generation|Code generation]] **[[Code generation overview]] **[[Debugging code generation]] * [[:Category:Emulator|Emulator]] ** [[Emulator overview]] * [[Getting started]] * [[:Category:Linking|Linking]] **[[ELF overview]] **[[ELF specification]] *[[Rationale]] *[[SDK]] *[[Current status|Status]] === Category tree (requires Ajax and Javascript) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} ffdf629143606fce33dfae7504afe12a1a106341 218 217 2021-06-03T17:06:38Z Jbyrd 1 wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Category tree (requires Ajax and Javascript) === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 1c617f3b41e088261a6b91c50205964bbadce544 219 218 2021-06-03T17:07:45Z Jbyrd 1 /* Category tree (requires Ajax and Javascript) */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out! === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 57152ad5445e9dc59ba5a791a1e56d5f36f84b4e 220 219 2021-06-03T17:12:02Z 104.189.8.152 0 Changed a period wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} f812f9aa3aa965cd6bb817e3d6544655a8aa11e7 227 220 2021-06-16T18:14:07Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[Category:Main]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} a25b30266412ac43e71f4a8aa3bfd0cfb2a67aec 230 227 2021-06-17T00:04:07Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 7862bee1b2af144660596210cba33093ea20acd8 231 230 2021-06-17T06:00:55Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} f04fbab6c3d100fe3e9e4e061dc24654366d7b34 235 231 2021-06-20T20:37:31Z 104.189.8.152 0 Edited a sentence wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} f24fd73bf0cb64e4eed8167aba5a65f368fe6e41 236 235 2021-06-20T20:37:55Z 104.189.8.152 0 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} f04fbab6c3d100fe3e9e4e061dc24654366d7b34 239 236 2021-07-01T23:16:20Z 71.198.117.145 0 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatibile with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 2adf4021f2d6e29331e96b67733a72cc831c1f9a 249 239 2021-07-16T20:51:42Z 71.198.117.145 0 Add trademark disclaimer. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatibile with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 1aeebcd17fe2496b4fbe6ae17f55495f0cf5626b 250 249 2021-07-19T19:47:51Z Jbyrd 1 Updated Slack group to non expiring. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatibile with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} e3b03c755adeff21541aa8396865023e3bb16bae 251 250 2021-07-19T21:15:22Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs, including the . The llvm-mos Clang is nearly compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 6fa014598f63a6aa51c6286c3cde354df7dce4d8 Category:Linking 14 37 207 2021-06-03T16:48:38Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 ELF overview 0 14 208 61 2021-06-03T16:49:02Z Jbyrd 1 wikitext text/x-wiki Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries. There exists a more precise [[ELF specification]] for llvm-mos. [[Category:Linking]] 21fa26fbced5206c87230311cfea30a56588e183 ELF specification 0 15 209 122 2021-06-03T16:49:20Z Jbyrd 1 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most signifcant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [[Category:Linking]] ab0dd4909e503adccd7dcc767701c195e7361647 Rationale 0 26 212 173 2021-06-03T17:01:54Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. And, for those programs that do require a soft stack, llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. [[Category:Main]] 43a01164f18f40956cf9856835164519939166eb 233 212 2021-06-20T08:30:33Z Jbyrd 1 wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes. By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes. This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts. Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation. And, for those programs that do require a soft stack, llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502. llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might. llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502. Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. == Findings == Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," it is not a good host for C. In truth, the 6502 is an excellent target for C, ''if'' the compiler is designed from the ground up to take advantage of the 6502's architecture. By that, I mean that the compiler must be aware of zero page, and it must be able to manage a user-specified chunk of it. The original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. llvm-mos uses an "imaginary register" abstraction to produce performant 6502 code, by permitting the compiler to manage that zero-page memory in a manner compatible with all existing 6502 targets. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. In fact, source language representation is not a limiting factor in generating performant 6502 code. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. All these "common knowledge" assumptions have been demonstrated to be unsupportable, by counterexample. [[Category:Main]] 7ad8424d85e0becb34521a56567f92ca7f8192bd 241 233 2021-07-01T23:43:13Z 71.198.117.145 0 /* Performance */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program. At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers. Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow). As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. == Findings == Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We presenting these to LLVM as registers, which makes our backend look roughly like a slightly odd backend, not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, there not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] cb55694452b692411b796ab881915f072756b032 242 241 2021-07-01T23:44:26Z 71.198.117.145 0 /* Findings */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program. At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers. Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow). As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. == Findings == Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, there not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] f6ca331258a2744fcb0e18cd1f28817f5bc03efa 243 242 2021-07-01T23:45:08Z 71.198.117.145 0 /* Findings */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program. At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers. Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow). As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. == Findings == Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] a8d8b9f9cf3a5d2d6d6f0deb3811cd98f5be0541 SDK overview 0 24 213 131 2021-06-03T17:02:39Z Jbyrd 1 Jbyrd moved page [[SDK]] to [[SDK overview]] without leaving a redirect wikitext text/x-wiki A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises all the platform-specific libraries, includes, and examples that can be helpful using llvm-mos with a specific target. 56ef83a903abddad85663e53dfa87f77e0080055 214 213 2021-06-03T17:02:54Z Jbyrd 1 wikitext text/x-wiki A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises all the platform-specific libraries, includes, and examples that can be helpful using llvm-mos with a specific target. [[Category:SDK]] fc724141d85b798d5ad28ec62cf0aea58de57d27 Category:SDK 14 38 215 2021-06-03T17:03:32Z Jbyrd 1 Created page with "[[Category:Main]]" wikitext text/x-wiki [[Category:Main]] f429e0e8fafc74c22b63006cc728e3bdd2e9c299 Current status 0 25 216 136 2021-06-03T17:04:10Z Jbyrd 1 wikitext text/x-wiki == Overview == '''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == C compiler == An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Currently, it also has notable codegen bugs that prevents it from being used for any but the smallest programs, but progress is ongoing. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 08e9fbebeedde9e852b2c29809830c44419b45d5 232 216 2021-06-20T08:22:36Z Jbyrd 1 /* C compiler */ wikitext text/x-wiki == Overview == '''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == C compiler == An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Recently, the compiler produced correct results on a slightly modified version of the gcc C torture test suite. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] c0dbd9f49e78c945c74f0c65546987de34a32eb9 237 232 2021-07-01T05:44:54Z 71.198.117.145 0 /* C compiler */ wikitext text/x-wiki == Overview == '''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == C compiler == An C99-complete backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still fairly good, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 4a08c02605bf0df5eabdf6f250e3cb7412a62a34 238 237 2021-07-01T05:45:19Z 71.198.117.145 0 /* C compiler */ wikitext text/x-wiki == Overview == '''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == C compiler == An C99-complete backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 52badda89a37baad2bd0f26ff8a14d527af03c39 C calling convention 0 17 221 185 2021-06-06T23:36:30Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the bytes are split and passed as above. * If no registers are available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 5874c9125567c4388b3c15c916e7987b2732eb2d 222 221 2021-06-06T23:36:59Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above, low byte first. * If no registers are available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] d7fa80068d97934353134703bfe2964831927285 223 222 2021-06-06T23:37:21Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers are available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 10c1c49e7ea74897399d46a645ab9635ede36b8c 224 223 2021-06-06T23:37:47Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 84b950781a84a38e8375337a496414a5077a9619 225 224 2021-06-09T16:45:17Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). Y is not used for arguments (but is still caller-save); it's reserved to help the compiler shuffle values into the appropriate locations around calls. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] d13747dcebcd52ab28f0f5b0be866b402a823901 226 225 2021-06-11T23:22:25Z 104.189.8.152 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 6cf1d2653319093a1119f4d67ad570e0e6ca34d2 244 226 2021-07-05T20:21:48Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. * Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 58b87dd738bf3e9d76240b422c28e96d17311589 245 244 2021-07-06T06:25:52Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * The very last imaginary pointer register is skipped, although it is still caller-saved. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. * Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 7651bb5b9a113e9c051e3fd34ce784cfb25718f3 246 245 2021-07-06T20:28:31Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. * Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 58b87dd738bf3e9d76240b422c28e96d17311589 Code generation overview 0 22 228 191 2021-06-16T23:56:10Z Jbyrd 1 wikitext text/x-wiki Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a small handful of 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. [[Category:Code generation]] f6a7fb4407b457c1e01605716ee4f948c77aadf2 File:Rust-hello-atari-800.png 6 39 229 2021-06-17T00:02:59Z Jbyrd 1 wikitext text/x-wiki Hello world and factorial calculation in Rust, for Atari 800, with proof of concept by mrk 2c220834e8a733bf20a16675f693daeab4377e3c Getting started 0 23 234 203 2021-06-20T10:33:06Z 104.189.8.152 0 /* Download the LLVM-MOS tools */ wikitext text/x-wiki WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS. To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk. For more information about this project, please see llvm-mos.org. For information about the current status of this project, please see [[Current status|Current status.]] To learn why this project exists, please see [[Rationale]]. = Getting started = == Download the LLVM-MOS tools == If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform: * [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS] * [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Linux] * [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows] These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality. == Or, build the LLVM-MOS tools == However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform. Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM. Once you meet those requirements, you may use the following formula within your build environment: === Clone the LLVM-MOS repository === On Linux and MacOS: <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> On Windows: <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> If you fail to use the --config flag as above, then verification tests will fail on Windows. === Configure the LLVM-MOS project === <code>cd llvm-mos cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code> This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment. Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake. * <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command. * <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. * <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). * <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file. * <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). === Build the LLVM-MOS project === <code>cmake --build build [-- [options] <target>]</code> The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution. CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. = Help us out = We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes. All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules. If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality. You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests. Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications. [[Category:Main]] 1a48d0086027c6f5bb9855216515cd83e42f68fc C compiler 0 18 240 186 2021-07-01T23:19:16Z 71.198.117.145 0 wikitext text/x-wiki A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. The Clang is nearly compatible with the freestanding portion of the C99 standard, with a couple caveats: * Although LLVM's (SingleSource) end-to-end test suite passes, we haven't finished auditing the compiler for C99 compatibility. It's likely already very compatible, but we haven't done the "full point inspection" yet. * No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. * The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines. [[Category:C]] d45d00232d1b221611a0258816ad69e81a31e528 C Inline Assembly 0 40 247 2021-07-16T16:44:51Z Mysterymath 3 Created page with "Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There a..." wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: 'o', 'v', '<', '>', and 'g' * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * 'a': The A register. * 'x': The X register. * 'y': The Y register. * 'R': One of A, X, or Y. * 'd': Either X or Y. [[Category:C]] 9577a35ad758f4e082622ed00ad287d5a73e5153 248 247 2021-07-16T16:52:46Z Mysterymath 3 wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. [[Category:C]] 760d9a23c80290b5a2b9e22826867cfc946f35fe C interrupts 0 41 252 2021-07-31T23:35:26Z Mysterymath 3 Created page with "== Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compi..." wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Large "Register" File === llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast. We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow. If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by two or more ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the main interrupt attribute mostly useful for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. === Program-Wide Calling Convention Modification === If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved. === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. c2c091ca5cdd3aec110b52d4aedf5975009c5795 253 252 2021-07-31T23:37:31Z Mysterymath 3 wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Large "Register" File === llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast. We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow. If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by two or more ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the main interrupt attribute mostly useful for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. === Program-Wide Calling Convention Modification === If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved. === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 0b53d70cf6db18feba7be26c91541bb3c5740ceb 254 253 2021-08-01T00:05:07Z Mysterymath 3 /* "interrupt_norecurse" Attribute */ wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Large "Register" File === llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast. We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow. If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the main interrupt attribute mostly useful for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. === Program-Wide Calling Convention Modification === If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved. === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 18d56dd1e1b0b6e047d353e1420cc20bbe3a8720 255 254 2021-08-01T00:07:26Z Mysterymath 3 /* "interrupt_norecurse" Attribute */ wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Large "Register" File === llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast. We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow. If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. === Program-Wide Calling Convention Modification === If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved. === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] e2eed37d10346913fb19b8ae0f17eb18031f7b92 Welcome 0 1 256 251 2021-08-13T16:43:19Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 11a1800b485ca954c562a9dad33480316d5b390f 273 256 2021-09-18T20:30:25Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang appears to be compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 469219ff97c9d171ce872d4cbd3f9d45c8cc5e59 299 273 2022-01-27T04:35:37Z 71.198.117.145 0 Update wording on the welcome page. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == The llvm-mos project is intended to create a first-class [https://github.com/llvm-mos backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [[Getting started]]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux]. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 90bbb9e727f2c897fad281c40ee148d4f4630560 304 299 2022-02-25T18:27:38Z 71.198.117.145 0 Update the welcome page in preparation for initial release. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 536020405a372f37ed9f53d49b41eb1d342f54bd ELF specification 0 15 257 209 2021-08-13T16:46:33Z Jbyrd 1 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.3 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |A PC-relative jump value. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [[Category:Linking]] 5a9eb95ce55d163e45be12d2f83480c6275f7974 274 257 2021-09-19T18:09:44Z Jbyrd 1 Better description of PCREL_8 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.4 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [[Category:Linking]] 537f39122d2dab5e6727ee34dbb0b77cee1eaa4f 296 274 2022-01-17T20:18:58Z Jackoalan 5 Add R_MOS_ADDR_ASCIZ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.4 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |14 |A null-terminated, decimal ASCII string of the value. Primary use case is for generating the BASIC header on C64. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [[Category:Linking]] 8bde310bc8751253c60dac3ce9772fb2164fa8ba 298 296 2022-01-17T20:27:12Z Jackoalan 5 .mos_addr_asciz reference wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.4 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |.mos_addr_asciz <expression>, <num-digit-chars> |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |14 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [[Category:Linking]] cf88593d6bd7aa3fb14167b95480f0a6a9df5336 Imaginary registers 0 42 258 2021-08-15T00:49:27Z Jbyrd 1 Created page with "The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify. Like all modern c..." wikitext text/x-wiki The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify. Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. Because "virtual registers" has another predefined meaning in LLVM land, we use the term '''imaginary register''' to refer to a byte in zero page memory. It's represented at codegen time by a symbol like __rc17 or __rs5, which is then translated to an actual memory address by the linker at link time. The number of imaginary registers is user-selectable. To decide how many registers LLVM-MOS should use, use the --num-zp-regs=64 flag to clang, replacing 64 with the number of usable zero page locations. clang will only emit references to imaginary registers in the range from __rc0 to __rc[N-1]. This is how LLVM-MOS can be made to generate optimal output for very different 65xx architectures. LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 6a115d12997969014e9eaa8b01eb2797e11f1c99 294 258 2021-11-16T07:37:06Z 71.198.117.145 0 wikitext text/x-wiki The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify. Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. Because "virtual registers" has another predefined meaning in LLVM land, we use the term '''imaginary register''' to refer to a byte in zero page memory. It's represented at codegen time by a symbol like __rc17 or __rs5, which is then translated to an actual memory address by the linker at link time. Presently, the compiler requires 16 imaginary pointers (two contiguous bytes each), and the compiler doesn't presently have the means to make use of more. In the future, the compiler should make use of otherwise unused sections of the zero page for temporaries, local, and global variables. LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 195555eb674ab8cb0ec439d1e60d9c3f0f776e9d C interrupts 0 41 259 255 2021-08-15T04:57:29Z 71.198.117.145 0 Remove calling convention shenanigans. wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] f476e420d2c2a3dee25b35dc6ee3c774d6060495 262 259 2021-08-17T04:36:34Z 71.198.117.145 0 /* "no_isr" Attribute */ wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly.<syntaxhighlight lang="6502tasm"> pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda __save_a pha lda __save_x pha lda __save_y pha lda __call_indir pha lda __call_indir+1 pha JSR body pla sta __call_indir+1 pla sta __call_indir pla sta __save_y pla sta __save_x pla sta __save_a pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 9a3e45e86efba9867c00a994db92bf63d01648fb 263 262 2021-08-17T04:41:29Z 71.198.117.145 0 /* Manual Interrupt Sequence */ wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm"> pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda __save_a pha lda __save_x pha lda __save_y pha lda __call_indir pha lda __call_indir+1 pha JSR body pla sta __call_indir+1 pla sta __call_indir pla sta __save_y pla sta __save_x pla sta __save_a pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 990a704efdeee37df0c6ef379f434c297702c671 264 263 2021-08-23T04:12:10Z 71.198.117.145 0 Update interrupt sequences. wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm"> cld pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda __save_a pha lda __save_y pha JSR body pla sta __save_y pla sta __save_a pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 097f5379748c1b416ad818503f0d153d85f5688b 276 264 2021-09-23T16:27:28Z 2620:0:1001:7810:7BC3:5E92:F4AA:B122 0 Update Manual Interrupt Sequence for new Riscy calling convention wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm"> cld pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda mos8(__rc6) pha lda mos8(__rc7) pha lda mos8(__rc8) pha lda mos8(__rc9) pha lda mos8(__rc10) pha lda mos8(__rc11) pha lda mos8(__rc12) pha lda mos8(__rc13) pha lda mos8(__rc14) pha lda mos8(__rc15) pha lda mos8(__rc16) pha lda mos8(__rc17) pha lda mos8(__rc18) pha lda mos8(__rc19) pha lda mos8(__rc20) pha lda mos8(__rc21) pha lda mos8(__rc22) pha lda mos8(__rc23) pha lda mos8(__rc24) pha lda mos8(__rc25) pha lda mos8(__rc26) pha lda mos8(__rc27) pha lda mos8(__rc28) pha lda mos8(__rc29) pha lda __save_a pha lda __save_y pha JSR body pla sta __save_y pla sta __save_a pla sta mos8(__rc29) pla sta mos8(__rc28) pla sta mos8(__rc27) pla sta mos8(__rc26) pla sta mos8(__rc25) pla sta mos8(__rc24) pla sta mos8(__rc23) pla sta mos8(__rc22) pla sta mos8(__rc21) pla sta mos8(__rc20) pla sta mos8(__rc19) pla sta mos8(__rc18) pla sta mos8(__rc17) pla sta mos8(__rc16) pla sta mos8(__rc15) pla sta mos8(__rc14) pla sta mos8(__rc13) pla sta mos8(__rc12) pla sta mos8(__rc11) pla sta mos8(__rc10) pla sta mos8(__rc9) pla sta mos8(__rc8) pla sta mos8(__rc7) pla sta mos8(__rc6) pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 06784d92c7ff1cd79b1559c924ba15149e49114d 277 276 2021-09-24T22:26:17Z 2620:0:1001:7810:688D:2EB8:E151:A477 0 Remove __save_a and __save_y; these are now RC28 and RC29. wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm"> cld pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda mos8(__rc6) pha lda mos8(__rc7) pha lda mos8(__rc8) pha lda mos8(__rc9) pha lda mos8(__rc10) pha lda mos8(__rc11) pha lda mos8(__rc12) pha lda mos8(__rc13) pha lda mos8(__rc14) pha lda mos8(__rc15) pha lda mos8(__rc16) pha lda mos8(__rc17) pha lda mos8(__rc18) pha lda mos8(__rc19) pha lda mos8(__rc20) pha lda mos8(__rc21) pha lda mos8(__rc22) pha lda mos8(__rc23) pha lda mos8(__rc24) pha lda mos8(__rc25) pha lda mos8(__rc26) pha lda mos8(__rc27) pha lda mos8(__rc28) pha lda mos8(__rc29) pha JSR body pla sta mos8(__rc29) pla sta mos8(__rc28) pla sta mos8(__rc27) pla sta mos8(__rc26) pla sta mos8(__rc25) pla sta mos8(__rc24) pla sta mos8(__rc23) pla sta mos8(__rc22) pla sta mos8(__rc21) pla sta mos8(__rc20) pla sta mos8(__rc19) pla sta mos8(__rc18) pla sta mos8(__rc17) pla sta mos8(__rc16) pla sta mos8(__rc15) pla sta mos8(__rc14) pla sta mos8(__rc13) pla sta mos8(__rc12) pla sta mos8(__rc11) pla sta mos8(__rc10) pla sta mos8(__rc9) pla sta mos8(__rc8) pla sta mos8(__rc7) pla sta mos8(__rc6) pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 3fd5af4818937eebe9a049365531050072fb64a7 C calling convention 0 17 260 246 2021-08-15T05:03:26Z 71.198.117.145 0 Adopt the interrupt friendly calling convention across the board. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The caller-saved registers are flags, A, X, Y, RC2, RC3, RC4, and RC5. A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in caller-saved registers, except flags and Y. (It's not worth trying to get arguments into and out of the flags, and Y isn't used to make it easier to set up outgoing arguments for function calls.) * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. * Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 361bb5d077a6d6b1e9c90be5f4fed489939a9566 268 260 2021-09-16T21:32:27Z 2620:0:1001:7810:66A0:16FD:D311:8D5E 0 Values can no longer be saved on the soft stack now that the number of required imaginary registers have increased. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The caller-saved registers are flags, A, X, Y, RC2, RC3, RC4, and RC5. A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in caller-saved registers, except flags and Y. (It's not worth trying to get arguments into and out of the flags, and Y isn't used to make it easier to set up outgoing arguments for function calls.) * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 45777404870f288127bb06f5377252d6380dd9fb 275 268 2021-09-23T16:04:51Z 2A00:79E1:ABC:1328:543B:D492:5964:D581 0 Update calling convention wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The caller-saved registers are flags, A, X, Y, and RS1 to RS14 (RC1 to RC29). A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC1 to RC29. The other caller saved registers are not used for arguments, to make writing compiler thunks easier and to allow efficiently setting up call arguments. * Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC0, RC1) to RS7=(RC14, RC15)). If none are available, the low and high bytes are split and passed as above. * If no registers remain available, any remaining bytes are passed through the soft stack. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] cec82d73c041fa7f1402d8f5dd85b04614a3759a 286 275 2021-10-15T02:27:03Z 71.198.117.145 0 Update calling convention. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The caller-saved registers are flags, A, X, Y, and RS1 to RS14 (RC1 to RC29). A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC1 to RC29. The other caller saved registers are not used for arguments, to make writing compiler thunks easier and to allow efficiently setting up call arguments. * Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC0, RC1) to RS7=(RC14, RC15)). * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] d3db27fdbe9fb362f1d9c41c5256b4bc2e4f3eff 287 286 2021-10-15T02:29:22Z 71.198.117.145 0 Correct calling convention. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * The caller-saved registers are flags, A, X, Y, and RS1 to RS14 (RC1 to RC29). A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. The other caller saved registers are not used for arguments, to make writing compiler thunks easier and to allow efficiently setting up call arguments. * Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC1, RC2) to RS7=(RC14, RC15)). * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 0ae2ed2ce953967e39e63616c64b2efe4bdf6ccf 295 287 2021-11-16T07:40:46Z 71.198.117.145 0 wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * A, X, Y, C, N, V, Z and RS1 to RS12 (RC2 to RC28) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS9 to RS15 (RC18 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC1, RC2) to RS7=(RC14, RC15)). * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates. * Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 6b59dd5e63cd8629ee011166d8f6eb7f8ab4642f Current status 0 25 261 238 2021-08-16T02:49:58Z 76.87.74.244 0 wikitext text/x-wiki == C compiler == The C compiler is LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available. The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz. Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. == Assembler == The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] bb4eae10ac9c5e3c30bb4470d23391b9faf5d599 266 261 2021-08-30T16:25:45Z Jbyrd 1 /* C compiler */ wikitext text/x-wiki == C compiler == The [[C compiler]] is LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available. The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz. Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] b93d2d405cea0a17d0a7b9c652ac3547d4b3d587 267 266 2021-08-30T16:26:49Z Jbyrd 1 /* C compiler */ wikitext text/x-wiki == C compiler == The [[C compiler]] is based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available. The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz. Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 0b59d7a354bb0dcb144a6197d17aa5afdd618332 269 267 2021-09-18T19:53:29Z Jbyrd 1 wikitext text/x-wiki == C compiler == The compiler is believed to be C99 compatible, but the quality of generated code is not yet at even a v0.1 level. The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz. The [[C compiler]] is based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available. Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility. == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 16a408bc5a83c63b64c1112e1ed3cf5e96ebfd05 C compiler 0 18 265 240 2021-08-25T16:08:18Z 71.198.117.145 0 wikitext text/x-wiki A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. The Clang is broadly compatible with the freestanding portion of the C99 standard, with one big caveat: * No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. Some of the GCC and Clang language extensions work, some don't. Similarly some of the C++ language features work, some don't. We haven't exhaustively catalogued this; there's a lot, and some are extremely obscure and nearly useless. Still, some compiler extensions are de-facto standard, so we'll call out a few caveats for those. Known Compiler Extension Caveats: * The GCC/Clang alignment directives work only for global and static variables, not automatic (local) variables. Aligning the stack pointer is tricky, since it requires use of a frame pointer; but the rigors of 6502 indexing cause our frame pointer to be useless for this purpose. To get this to work, we may need *two* frame pointers. Expected call for this feature is low, so if you need it, let us know, and we'll reprioritize. [[Category:C]] 9e33e649df37e1b2b73eaf1b81c36aad4f966e3a Findings 0 43 270 2021-09-18T20:26:01Z Jbyrd 1 Created page with "Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, su..." wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. 8bea2e5dce4c1b45094546912c901cbaa8c3cb6f 271 270 2021-09-18T20:27:17Z Jbyrd 1 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] 9cf9bafda2ed6e975a65d9148cde109fcdc2e90e 278 271 2021-09-27T05:04:06Z Jbyrd 1 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] 9ea63ba6c04bd69793ff0b0770d45594ffcfede5 279 278 2021-09-27T05:04:27Z Jbyrd 1 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] b2015a6b44bc02bb4c5c40db022a1d04b350539a 280 279 2021-10-08T15:25:00Z 2603:8000:3F44:8EAC:1811:8E3E:DD8D:BA82 0 Added missing word wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] 211c5d4acc965b4bff6a73dd88256b1f85ee5acc 281 280 2021-10-11T20:23:31Z 209.79.16.103 0 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] ef006a43fdcef9e38c335ab52b284276e6162bee 282 281 2021-10-11T20:26:24Z 209.79.16.103 0 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code and interrupt-driven code, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] e047a19e3431977a7c41e2fe4821376788374b4b 283 282 2021-10-11T20:27:03Z 209.79.16.103 0 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code and interrupt-driven code, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] 6350469981fb21663cf63c9056373cc3bacce3a5 284 283 2021-10-11T20:27:48Z 209.79.16.103 0 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code and interrupt-driven code, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] b012598de438fe5fbfcfcc94b33585b240746163 285 284 2021-10-11T20:29:06Z 209.79.16.103 0 wikitext text/x-wiki Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work. First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so. Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C. Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained. Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502. Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats. Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work. Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later. Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502. [[Category:Main]] 49bc9b85ac6f0277f3f1908625f8cbbfd3922f8a 288 285 2021-10-17T04:58:39Z 71.198.117.145 0 Update findings page. wikitext text/x-wiki Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. == Stacks == Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. == Registers == The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. [[Category:Main]] 2171799d6474f9cbc969204e79e4c7d521b1d3cf 289 288 2021-10-19T20:23:36Z 2620:15C:2D1:203:4B9E:24FF:290D:9B8C 0 Mention other non-codegen advantages of LLVM wikitext text/x-wiki GEfficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. == Stacks == Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. == Registers == The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. === ELF === Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. === GAS Assembler === The assembler's directives are broadly compatible GNU Assembler; as such we have access to all the features that come with it. Weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang; since it uses the underlying assembler to interpret these fragments. === Non-C languages === LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. [[Category:Main]] 2b2f38faf9715b51eceeff965ba67d28f48d2801 290 289 2021-10-25T18:28:27Z 107.195.41.193 0 wikitext text/x-wiki Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. == Stacks == Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. == Registers == The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. === ELF === Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. === GAS Assembler === The assembler's directives are broadly compatible GNU Assembler; as such we have access to all the features that come with it. Weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang; since it uses the underlying assembler to interpret these fragments. === Non-C languages === LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. [[Category:Main]] 147abe7d0eab985a0eb4f6baa21cd3a62f0859e2 291 290 2021-10-25T18:30:07Z 107.195.41.193 0 /* GAS Assembler */ wikitext text/x-wiki Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. == Stacks == Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. == Registers == The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. === ELF === Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. === GAS Assembler === The assembler's directives are broadly compatible with GNU Assembler; as such we have access to all the features that come with it. Weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments. === Non-C languages === LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. [[Category:Main]] bf34f793e29744f3267196bd24a2da7b9e590775 292 291 2021-10-25T18:37:10Z 107.195.41.193 0 /* GAS Assembler */ wikitext text/x-wiki Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. == Stacks == Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. == Registers == The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. === ELF === Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. === GAS Assembler === The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments. === Non-C languages === LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. [[Category:Main]] 3f4a2a2f5b7b9e7f2536ca2a11a1f159b3af05b4 293 292 2021-10-25T18:40:28Z 107.195.41.193 0 wikitext text/x-wiki == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. === Stacks === Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. === Registers === The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == ELF == Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. == GAS Assembler == The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments. == Non-C languages == LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. [[Category:Main]] 396854bfd5ab0ec7efd1c7fce94d94ded3b6aea6 Rationale 0 26 272 243 2021-09-18T20:28:06Z Jbyrd 1 /* Findings */ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program. At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers. Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow). As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. See also [[Findings]]. [[Category:Main]] f048fc775241c31c9ddb0dd3b16b0ca74fd91c4c 303 272 2022-02-24T23:01:59Z 2620:15C:2D1:203:B7B0:54A:7A64:FBA5 0 Removed number of zp regs customizabiilty. wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program. At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers. Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow). As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. See also [[Findings]]. [[Category:Main]] b5e2cfbc99da4b1a895785c03af53e9ef77c4386 Assembler 0 13 297 162 2022-01-17T20:24:16Z Jackoalan 5 mos_addr_asciz wikitext text/x-wiki The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. === Target-specific directives === {| class="wikitable" |+ !Syntax !Description |- | .mos_addr_asciz <expression>, <num-digit-chars> |Emits a fixed-length decimal ASCII string of a relocatable expression. Primary use case is for generating the BASIC header on C64. |} [[Category:Assembly]] 0c1b7ab42308dd414505c14912e2525280c545ed Getting started 0 23 300 234 2022-01-27T04:38:40Z 71.198.117.145 0 Soften the warning; we're getting closer. wikitext text/x-wiki NOTICE: There's still much work to be optimization work to be done before our first official release. Please don't publicly review, compare, or benchmark against other compilers at this. To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk. For more information about this project, please see llvm-mos.org. For information about the current status of this project, please see [[Current status|Current status.]] To learn why this project exists, please see [[Rationale]]. = Getting started = == Download the LLVM-MOS tools == If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform: * [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS] * [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Linux] * [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows] These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality. == Or, build the LLVM-MOS tools == However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform. Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM. Once you meet those requirements, you may use the following formula within your build environment: === Clone the LLVM-MOS repository === On Linux and MacOS: <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> On Windows: <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code> If you fail to use the --config flag as above, then verification tests will fail on Windows. === Configure the LLVM-MOS project === <code>cd llvm-mos cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code> This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment. Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake. * <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command. * <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. * <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>). * <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file. * <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types). === Build the LLVM-MOS project === <code>cmake --build build [-- [options] <target>]</code> The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution. CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target. Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have. = Help us out = We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes. All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules. If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality. You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests. Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications. [[Category:Main]] d326aba630a5223500ee9fbb1504f79454650f23 305 300 2022-02-25T18:30:39Z Mysterymath 3 One source of truth for getting started. wikitext text/x-wiki See [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting Stated with the SDK]. [[Category:Main]] 65e596c6df8aed2c2342943ff3664219b308154f C volatile 0 44 301 2022-02-18T02:14:55Z 71.198.117.145 0 Add C volatile page. wikitext text/x-wiki The compiler will freely issue indexed addressing modes wherever appropriate. Due to a processor bug, these addressing modes can cause NMOS 6502 to issue a spurious read to a memory location one page lower than the one intended. These reads are not considered to constitute an access to a volatile object, even if they overlap with one in memory. To avoid accidentally triggering I/O, pointer arithmetic should be avoided for addresses one page above read-sensitive I/O locations. To be safe, such routines can be written in assembly. The read/modify/write (RMW) instructions (e.g., INC) also issue a spurious read or write to the location, depending on the 6502 version. If the underlying object is volatile, these accesses would be considered to be volatile accesses; accordingly, the compiler avoids generating RMW instructions for such addresses. Instead, the compiler will manually issue the load, manipulate the value, and store it back. 544f3a6e001e8620c473dc853087ed4a76cd4c10 302 301 2022-02-18T02:15:33Z 71.198.117.145 0 Add category. wikitext text/x-wiki The compiler will freely issue indexed addressing modes wherever appropriate. Due to a processor bug, these addressing modes can cause NMOS 6502 to issue a spurious read to a memory location one page lower than the one intended. These reads are not considered to constitute an access to a volatile object, even if they overlap with one in memory. To avoid accidentally triggering I/O, pointer arithmetic should be avoided for addresses one page above read-sensitive I/O locations. To be safe, such routines can be written in assembly. The read/modify/write (RMW) instructions (e.g., INC) also issue a spurious read or write to the location, depending on the 6502 version. If the underlying object is volatile, these accesses would be considered to be volatile accesses; accordingly, the compiler avoids generating RMW instructions for such addresses. Instead, the compiler will manually issue the load, manipulate the value, and store it back. [[Category:C]] a65e42a03ddaafb1dba8789791728594b4c2a8e9 Current status 0 25 306 269 2022-02-25T18:34:08Z Mysterymath 3 Update status. wikitext text/x-wiki == C compiler == The compiler is broadly freestanding C99/C++ compatible (notably excepting float/double and C++ exceptions). It emits reasonably good code. The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz. == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 1a1d11f6c78c4c34ce320ac5df63f9774f80dfd1 307 306 2022-02-25T18:38:21Z Mysterymath 3 Add in info from first release. wikitext text/x-wiki == C compiler == * Code generation for the Commodore 64, Atari 800, and an included 6502 simulator. * The high and low-level optimizations expected of a young-ish LLVM backend ** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers ** The imaginary registers can be placed anywhere and need not be contiguous. ** The calling convention passes through registers whenever possible. ** Loop optimizations to select 6502 addressing modes ** Whole program "static stack" optimization *** Automatically identifies non-reentrant functions and allocates their frames as static globals *** Programs without recursion or complex function pointers may not need a soft stack at all. *** No manual annotations required ** Link time inlining and optimization across the whole program *** Includes SDK libraries. Library calls can be often optimized away completely! * Broad C99 and C++11 freestanding standards compatibility ** Interrupt handling ** C++ templates ** C++ virtual functions ** C++ new/delete ** C++ Run-Time Type Information (dynamic_cast, typeid) ** C++ static constructors/destructors (run before and after main) ** C++ "magic" function local static constructors/destructors * Excellent compiler usability ** Clang's world-class error messages ** IDE integration through the included custom clangd's Language Server Protocol ** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code> * A small standard library sufficient to provide the above and a few extras ** Simple printf ** Simple malloc/free ** exit, _Exit, and atexit == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] ddd78caee471fc9a4df498b096eca21b799a199c 308 307 2022-02-25T18:40:27Z Mysterymath 3 Add exceptions wikitext text/x-wiki == C compiler == * Code generation for the Commodore 64, Atari 800, and an included 6502 simulator. * The high and low-level optimizations expected of a young-ish LLVM backend ** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers ** The imaginary registers can be placed anywhere and need not be contiguous. ** The calling convention passes through registers whenever possible. ** Loop optimizations to select 6502 addressing modes ** Whole program "static stack" optimization *** Automatically identifies non-reentrant functions and allocates their frames as static globals *** Programs without recursion or complex function pointers may not need a soft stack at all. *** No manual annotations required ** Link time inlining and optimization across the whole program *** Includes SDK libraries. Library calls can be often optimized away completely! * Broad C99 and C++11 freestanding standards compatibility ** Interrupt handling ** C++ templates ** C++ virtual functions ** C++ new/delete ** C++ Run-Time Type Information (dynamic_cast, typeid) ** C++ static constructors/destructors (run before and after main) ** C++ "magic" function local static constructors/destructors * Excellent compiler usability ** Clang's world-class error messages ** IDE integration through the included custom clangd's Language Server Protocol ** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code> * A small standard library sufficient to provide the above and a few extras ** Simple printf ** Simple malloc/free ** exit, _Exit, and atexit === Notable Exceptions === * C99 requires supporting 64KiB locals * Hosted C with all the standard library bells and whistles. * Float/double * C++ Exceptions == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] bb2050d0834bd002e2ced3096562adc9902932b5 C calling convention 0 17 309 295 2022-03-08T06:11:09Z 71.198.117.145 0 Update calling convention to make small aggregates passed by value. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * A, X, Y, C, N, V, Z and RS1 to RS12 (RC2 to RC28) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS9 to RS15 (RC18 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC1, RC2) to RS7=(RC14, RC15)). * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 43fbc9dc3b6b5596100935b793effe22979fc171 326 309 2022-05-18T08:13:30Z 167.98.40.6 0 Correct the calling convention description. wikitext text/x-wiki The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler. * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 98aa04c89b3521639998adf72b4f2b2d9e0b908e Linker Script 0 45 310 2022-03-19T20:05:10Z Mysterymath 3 Add linker script description. wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts. See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior. This page describes LLVM-MOS's extensions of LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code> * A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code> Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the section are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that the output section doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. d5eed62a81a647f6079e89985035d2d38c7c5f6f 311 310 2022-03-19T20:07:44Z Mysterymath 3 Add to category. wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts. See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior. This page describes LLVM-MOS's extensions of LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code> * A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code> Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the section are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that the output section doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. [[Category:Linking]] b9008199cd41b5bfb1f82b55ae44b795f62f8b5a 312 311 2022-03-21T01:26:06Z 71.198.117.145 0 Clarify section vs region. wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts. See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior. This page describes LLVM-MOS's extensions of LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code> * A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code> Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. [[Category:Linking]] 0cfa07ba332911eb0b31d8fd473e314a8b6bdd48 Welcome 0 1 313 304 2022-04-15T02:15:43Z 71.198.117.145 0 Add NES mention wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] == Welcome to the llvm-mos project! == llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} 7a9418d53c3ddb0bc6f151d7e553ee2301723adb 315 313 2022-04-19T18:01:12Z Jbyrd 1 Added OSC1P wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] == Welcome to the llvm-mos project! == llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ==== Notice ==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. === Category tree === <CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree> === Categories === {{Special:AllPages|namespace=14}} === Pages === {{Special:AllPages}} c8d22227c9e46d1e57c772712b645a0ca61f7b86 336 315 2022-07-06T17:37:18Z Jbyrd 1 Added Steven Poulton's NES project wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] ==Welcome to the llvm-mos project!== llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 6a615b90fdf3bfac0dbb51c07f52d78b5f62af57 350 336 2022-08-02T14:45:21Z Mysterymath 3 Add SDK doxygen link. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] ==Welcome to the llvm-mos project!== llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} f3ca94eac06b8b4dd7b8379b584bbf4069b62a1b 354 350 2022-08-12T06:12:02Z Wombat 9 Add Commander X16 screenshot wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]NES project in pure C99 using llvm-mos, by Steven Poulton]] ==Welcome to the llvm-mos project!== llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 997002fc7e71f70f78fe507010b4b8bae8e5beee 355 354 2022-08-12T06:18:45Z Wombat 9 Move Commander X16 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 2010dcbec75d858e1ba0cd93a0025100bad2bc14 File:Llvm-mos hello world osi c1p.png 6 46 314 2022-04-19T17:59:42Z Jbyrd 1 wikitext text/x-wiki Hello world in C, targeting Ohio Scientific Challenger 1P 9a86dbf8ae9e8cc93f9eb6bd4f9f29857bcfbbd4 Porting 0 47 316 2022-04-23T02:17:26Z 71.198.117.145 0 Start working on porting guide; not yet finished, but don't want to lose changes. wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == Let's try again!<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight> 50ebc990e4ffd635df72f9eda7b5069239028aff 317 316 2022-04-23T02:34:51Z Mysterymath 3 Add ELF information. wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == Let's try again!<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>Okay, so that compiled! Unfortunately, it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). This is because of the rich information about the contents provided by this format; this in turn allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == 30ba63f5a01c3360433dfd65d99bd563abf4a52a 318 317 2022-04-23T02:50:11Z Mysterymath 3 /* Output Format */ wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>. Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>. == Compiling: Third Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main main.elf ... $ file main main:data $ file main.elf main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped $ hexdump -C main 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00010000 00 02 |..| 00010002 </syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script. Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>. f01af42c583a9402a133527ad8c4e7b656807263 319 318 2022-04-24T00:59:38Z Mysterymath 3 Optional Libraries wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>. Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>. == Compiling: Third Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main main.elf ... $ file main main:data $ file main.elf main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped $ hexdump -C main 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00010000 00 02 |..| 00010002 </syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script. Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>. == Optional Libraries == Take another look at the disassembly:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land. This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors). Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target. The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target]. One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities: {| class="wikitable" !Exit Library !Description |- |<code>exit-custom</code> |Run some custom code (e.g., execute a special BRK, which goes into an underlying OS). |- |<code>exit-loop</code> |Go into an infinite loop. |- |<code>exit-return</code> |Return from <code>_start</code> |} Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>. There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you. We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0 OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Nothing changed! Is there a problem? Nope! Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine. 82e5466f72689d6516094af0ba4d4ffa897763d2 320 319 2022-04-24T01:07:17Z Mysterymath 3 wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>. Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>. == Compiling: Third Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main main.elf ... $ file main main:data $ file main.elf main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped $ hexdump -C main 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00010000 00 02 |..| 00010002 </syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script. Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>. == Optional Libraries == Take another look at the disassembly:<syntaxhighlight lang="shell-session"> main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land. This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors). Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target. The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target]. One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities: {| class="wikitable" !Exit Library !Description |- |<code>exit-custom</code> |Run some custom code (e.g., execute a special BRK, which goes into an underlying OS). |- |<code>exit-loop</code> |Go into an infinite loop. |- |<code>exit-return</code> |Return from <code>_start</code> |} Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>. There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you. We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0 OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Nothing changed! Is there a problem? Nope! Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine. 0ce1b107c9511e89975ba5774c2c258af4abc29a 324 320 2022-04-28T17:31:21Z Mysterymath 3 wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>. Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>. == Compiling: Third Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main main.elf ... $ file main main:data $ file main.elf main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped $ hexdump -C main 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00010000 00 02 |..| 00010002 </syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script. Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>. == Optional Libraries == Take another look at the disassembly:<syntaxhighlight lang="shell-session"> main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land. This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors). Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target. The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target]. One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities: {| class="wikitable" !Exit Library !Description |- |<code>exit-custom</code> |Run some custom code (e.g., execute a special BRK, which goes into an underlying OS). |- |<code>exit-loop</code> |Go into an infinite loop. |- |<code>exit-return</code> |Return from <code>_start</code> |} Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>. There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you. We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0 OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Nothing changed! Is there a problem? Nope! Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.' = Extending the SDK = After porting LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated. To do so, continue onward to the [[Extending SDK]] guide. 0f75cac25450f8b096c5e81f23abaa2b54d5a099 331 324 2022-06-15T04:23:06Z Pyrex8 8 ; need at end of line otherwise: ld.lld: error: link.ld:15: ; expected, but got OUTPUT_FORMAT >>> OUTPUT_FORMAT { FULL(ram) SHORT(_start) } >>> ^ wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>. Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>. == Compiling: Third Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main main.elf ... $ file main main:data $ file main.elf main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped $ hexdump -C main 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00010000 00 02 |..| 00010002 </syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script. Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>. == Optional Libraries == Take another look at the disassembly:<syntaxhighlight lang="shell-session"> main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land. This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors). Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target. The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target]. One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities: {| class="wikitable" !Exit Library !Description |- |<code>exit-custom</code> |Run some custom code (e.g., execute a special BRK, which goes into an underlying OS). |- |<code>exit-loop</code> |Go into an infinite loop. |- |<code>exit-return</code> |Return from <code>_start</code> |} Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>. There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you. We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0; OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Nothing changed! Is there a problem? Nope! Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.' = Extending the SDK = After porting LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated. To do so, continue onward to the [[Extending SDK]] guide. ab2b3386b992e432fc58053067aeea1393cce5a0 Extending SDK 0 48 321 2022-04-27T23:28:41Z Mysterymath 3 Start SDK upstreaming guide. wikitext text/x-wiki After [[Porting]] LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated. This guide follows the Porting Guide above; make sure you've at least skimmed it before continuing, otherwise this guide likely won't make as much sense. bd3d573a1f3631b2b1536d8528e7cda576c688ad 322 321 2022-04-27T23:36:15Z Mysterymath 3 wikitext text/x-wiki After [[Porting]] LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated. This guide follows the Porting Guide above; make sure you've at least skimmed it before continuing, otherwise this guide likely won't make as much sense. == Review == Completing the porting guide produced the following artifacts: A linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0 OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>A command line to build a binary: <code>mos-common-clang -o main -Os main.c -lexit-loop -linit-stack</code> This is enough to set up a freestanding platform in the SDK. == CMake == As mentioned previously, our SDK uses CMake to build. If you haven't done so already, check out and build the [https://github.com/llvm-mos/llvm-mos-sdk SDK sources] using CMake. Let's call our platform <code>xyzzy</code>, for old times' sake. Make a <code>xyzzy</code> subdirectory under the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform mos-platform] directory for the platform to live in. Also, create an empty <code>CMakeLists.txt</code> file in that directory; that will eventually tell CMake how to build the platform. Additionally, tell CMake that the new directory exists by adding <code>add_subdirectory(xyzzy)</code> to [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/CMakeLists.txt mos-platform/CMakeLists.txt]. == Platform Definition == 2edc5f5fba9dc69b2122af435e7a01fdf671a111 323 322 2022-04-28T17:29:41Z Mysterymath 3 Finish. wikitext text/x-wiki After [[Porting]] LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated. This guide follows the Porting Guide above; make sure you've at least skimmed it before continuing, otherwise this guide likely won't make as much sense. == Review == Completing the porting guide produced the following artifacts: A linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0 OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>A command line to build a binary: <code>mos-common-clang -o main -Os main.c -lexit-loop -linit-stack</code> This is enough to set up a freestanding platform in the SDK. == CMake == As mentioned previously, our SDK uses CMake to build. If you haven't done so already, check out and build the [https://github.com/llvm-mos/llvm-mos-sdk SDK sources] using CMake. Let's call our platform <code>xyzzy</code>, for old times' sake. Make a <code>xyzzy</code> subdirectory under the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform mos-platform] directory for the platform to live in. Tell CMake that the new directory exists by adding <code>add_subdirectory(xyzzy)</code> to [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/CMakeLists.txt mos-platform/CMakeLists.txt]. This will cause CMake to look for a <code>CMakeLists.txt</code> file in the <code>xyzzy</code> subdirectory, which will tell it how to build the <code>xyzzy</code> platform. == Platform Definition == A minimal platform <code>CMakeLists.txt</code> for <code>xyzzy</code> is just <code>platform(xyzzy)</code>. Building and installing the SDK afterwards will then produce a <code>mos-xyzzy-clang</code> symlink that tells Clang to use the platform. This produces an incomplete target with nothing in it. Our target should be complete though; it should be possible to produce binaries from it. So we can tell CMake that by using <code>platform(xyzzy COMPLETE)</code>. This will cause CMake to link for a <code>link.ld</code> file in the <code>xyzzy</code> subdirectory. Placing the <code>link.ld</code> file there allows the build and install to succeed and causes <code>mos-xyzzy-clang</code> to automatically use that linker script. This still doesn't allow producing binaries though; the command line used for this was <code>mos-common-clang</code>, not <code>mos-clang</code>. Since the <code>xyzzy</code> platform is based off of the <code>common</code> platform, we need to change the platform definition to <code>platform(xyzzy COMPLETE PARENT common)</code>. With this, <code>mos-xyzzy-clang</code> can produce executables. == Platform Libraries == There's one remaining part of the command line to deal with: <code>-lexit-loop and -linit-stack</code>. These libraries need to be included in the platform libraries for <code>xyzzy</code>. For each platform, the Clang driver expects the following files to be available to the linker: {| class="wikitable" !File !Description |- |<code>link.ld</code> |Linker Script |- |<code>crt0.o</code> |Mandatory entry/exit functionality |- |<code>libcrt0.a</code> |Optional entry/exit functionality |- |<code>libcrt.a</code> |C runtime |- |<code>libc.a</code> |C standard library |} <code>link.ld</code> has already been discussed. <code>crt0.o</code> and <code>libcrt0.a</code> contain routines used at the beginning and end of a program. <code>libcrt0.a</code> contains functionality that may or may not be included, depending on what symbols are referenced in the program, while <code>crt0.o</code> contains the essential scaffolding to start and end a program. <code>libcrt.a</code> contains routines used to implement compiler functionality like multiplication and division, while <code>libc.a</code> contains the C standard library and support for some C++ language features. We can't just include <code>-lexit-loop</code> and <code>-linit-stack</code> on the command line; the above files are structured so that compiler flags like <code>-nostartfiles</code> do what they logically should. So, we need to include <code>libexit-loop.a</code> and <code>libinit-stack.a</code> into the appropriate platform library, <code>libcrt0.a</code>. To do so, we add to the <code>xyzzy</code> <code>CMakeLists.txt</code> :<syntaxhighlight lang="cmake"> platform(xyzzy COMPLETE PARENT common) if(NOT CMAKE_CROSSCOMPILING) return() endif() add_platform_library(xyzzy-crt0) merge_libraries(xyzzy-crt0 common-crt0 common-init-stack common-exit-loop ) </syntaxhighlight>The early return just makes sure the following libraries aren't compiled for the host; they're 6502 code, so they need to be compiled with llvm-mos. The remainder of the code creates a <code>libcrt0.a</code> for <code>xyzzy</code> that bundles in the <code>libcrt0.a</code>, <code>libinit-stack.a</code> , and <code>libexit-loop.a</code> libraries from the <code>common</code> platform. The other libraries bleed through from the <code>common</code> platform, while the <code>libcrt0.a</code> for <code>xyzzy</code> will be used in preference to the one from <code>libcommon</code> With this, <code>mos-xyzzy-clang</code> will behave like to the original command line, and the platform is now fully incorporated into the SDK. 495d82a6a334e42aad5a045cf9e858d1e3dc7055 Modifiers 0 16 325 169 2022-04-29T21:47:36Z 71.198.117.145 0 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar). [[Category:Assembly]] a206651555e4666ba44d36329fcb347e7cd62ae9 Frequently asked questions 0 49 327 2022-05-18T08:40:08Z 167.98.40.6 0 Add a FAQ page. wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". 2d03cb607ce2149dc690a039c2543ed05d4a6b57 333 327 2022-06-28T04:07:22Z 71.198.117.145 0 Add FAQ entry for missing sections. wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. 842194b19c2291c8684577effc56c146aaddb027 337 333 2022-07-13T04:48:25Z Mysterymath 3 Add a guide for dealing with inline assembly to the FAQ. wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char a) { asm ("some compiler-unknown string of inline assembly goes here"); return a; } </syntaxhighlight>The calling convention has the value <code>a</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char a) { asm ("some compiler-unknown string of inline assembly goes here"::"a"); return a; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void bar(char a); void foo(char a) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>a</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>a</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void bar(char a); void foo(char a) { asm volatile ("st%0 0x1234"::"R"(a)); } </syntaxhighlight>The <code>R</code> constraint on the <code>a</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. 155fa404d653a68941c2de03825c08e4c441989c 338 337 2022-07-13T04:52:47Z Mysterymath 3 Move to Main category. wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char a) { asm ("some compiler-unknown string of inline assembly goes here"); return a; } </syntaxhighlight>The calling convention has the value <code>a</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char a) { asm ("some compiler-unknown string of inline assembly goes here"::"a"); return a; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void bar(char a); void foo(char a) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>a</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>a</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void bar(char a); void foo(char a) { asm volatile ("st%0 0x1234"::"R"(a)); } </syntaxhighlight>The <code>R</code> constraint on the <code>a</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. [[Category:Main]] 7f5b19a45886721a265d31efd6ba3d90b42c6737 341 338 2022-07-19T05:01:32Z Mysterymath 3 Fix inline assembly FAQ wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>a</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>a</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234"::"R"(value)); } </syntaxhighlight>The <code>R</code> constraint on the <code>a</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. [[Category:Main]] 9d9fdd84575c641e6e6da8114a1bf34ce21c6f77 342 341 2022-07-19T07:57:38Z Wombat 9 Changed label "a" to "value". Added extra ":" before clobber. wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234"::"R"(value)); } </syntaxhighlight>The <code>R</code> constraint on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. [[Category:Main]] 8025f097a4b41ec4acfccb10920ed36db637db0d Code generation overview 0 22 328 228 2022-06-07T17:05:24Z Jbyrd 1 Added YouTube talk wikitext text/x-wiki See also [https://www.youtube.com/watch?v=2lW3WHPtmKo this talk at 2022 EuroLLVM], which gives a high-level overview of llvm-mos's code generation strategy. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a small handful of 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. [[Category:Code generation]] 458e4a9c5460dd8f716eed188e894897b9a6115d Assembler relaxation 0 19 329 167 2022-06-08T04:42:22Z Stm 7 Fixed typo: The special section name for the zero page is ".zp" and not "zp" wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section .zp low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x</code> A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"z",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] fcae9575990ea1d661757a1bd4140eadee8ce438 330 329 2022-06-09T21:02:42Z Mysterymath 3 Add allocatable flags to zp example. wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section .zp,"a",@nobits low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"az",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] c0f4143c142143dcc75841ccb4457e89057fef98 Rust 0 50 332 2022-06-26T17:50:44Z Mrk 6 Created blank page wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 343 332 2022-07-19T08:54:23Z Wombat 9 Added the first Rust information wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost). ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE. === Other resources === * For commodore targets like c64 and mega65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust"> use mos_hardware::{c64}; let old_border_color = (*c64::VIC).border_color.read(); </syntaxhighlight> 431ae92fc6d5ddbd3b01ee5d99d4b1f156f46807 344 343 2022-07-19T09:04:30Z Wombat 9 Capital letters wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost). ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE. === Other resources === * For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust"> use mos_hardware::{c64}; let old_border_color = (*c64::VIC).border_color.read(); </syntaxhighlight> d4267dd98b81f2127d72e6f19293cbdb81f73683 345 344 2022-07-19T09:08:31Z Wombat 9 Update example wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost). ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE. === Other resources === * For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust"> use mos_hardware::{c64, vic2} (*c64::VIC).border_color.write(vic2::LIGHT_RED); </syntaxhighlight> e12a8307fa0ab5abdc26fb834ce8975bfa5a785d 346 345 2022-07-19T09:13:34Z Wombat 9 /* Other resources */ Add examples wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost). ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE. === Other resources === * For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust"> use mos_hardware::{c64, vic2} (*c64::VIC).border_color.write(vic2::LIGHT_RED); </syntaxhighlight> A set of [https://github.com/mlund/mos-hardware/tree/main/examples code examples are available] (plasma effect, raster interrupt) 3a729b88887e08abc161d2a5d41b4f8b784df98d 347 346 2022-07-19T10:46:02Z Wombat 9 Added atari utility crate wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost). ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE. === Other resources === * Utility [https://github.com/mrk-its/a800xl-utils crate for for Atari 800XL]. * For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust"> use mos_hardware::{c64, vic2} (*c64::VIC).border_color.write(vic2::LIGHT_RED); </syntaxhighlight> A set of [https://github.com/mlund/mos-hardware/tree/main/examples code examples are available] (plasma effect, raster interrupt) d1bbc9f78d593c1ab10776fbdb3fc8c9eb020fb4 348 347 2022-07-19T11:44:52Z Wombat 9 Update docker info wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust"> use mos_hardware::{c64, vic2} (*c64::VIC).border_color.write(vic2::LIGHT_RED); </syntaxhighlight> A set of [https://github.com/mlund/mos-hardware/tree/main/examples code examples are available]. ede2e9119e12cd41c9a57dcf976aa5b2f041c23f 351 348 2022-08-03T06:57:37Z Wombat 9 Made the mos-hardware entry slightly less verbose wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). c6d0ced4632f538a2f132b20d546ede40b40db61 352 351 2022-08-09T11:39:42Z Wombat 9 Added examples wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. 209367b19d8fd1555a0ae7e9f56da500610b071f C Inline Assembly 0 40 334 248 2022-07-03T03:33:52Z Mysterymath 3 Add C and V flags to inline assembly. wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. [[Category:C]] 257dd32539a67ac8c81220a6675a40905c5e5ab2 339 334 2022-07-13T04:53:57Z Mysterymath 3 wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see the FAQ entry: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] [[Category:C]] 4f761f6a9b199228992bb85e3f5ddc34249a9643 340 339 2022-07-13T04:54:14Z Mysterymath 3 wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] [[Category:C]] 73a87c35e241a4f93d5a1d06a2f681a2151e8e1b File:Nes-llvm-demo.png 6 51 335 2022-07-06T17:36:02Z Jbyrd 1 wikitext text/x-wiki NES project in pure C99 using llvm-mos, by Steven Poulton 14e9406f1ebf49d7c618a05b7e970d34d1a55719 C interrupts 0 41 349 277 2022-07-26T21:06:25Z 34.214.158.144 0 /* Manual Interrupt Sequence */ fix minor grammar error wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is written entirely in assembly. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm"> cld pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda mos8(__rc6) pha lda mos8(__rc7) pha lda mos8(__rc8) pha lda mos8(__rc9) pha lda mos8(__rc10) pha lda mos8(__rc11) pha lda mos8(__rc12) pha lda mos8(__rc13) pha lda mos8(__rc14) pha lda mos8(__rc15) pha lda mos8(__rc16) pha lda mos8(__rc17) pha lda mos8(__rc18) pha lda mos8(__rc19) pha lda mos8(__rc20) pha lda mos8(__rc21) pha lda mos8(__rc22) pha lda mos8(__rc23) pha lda mos8(__rc24) pha lda mos8(__rc25) pha lda mos8(__rc26) pha lda mos8(__rc27) pha lda mos8(__rc28) pha lda mos8(__rc29) pha JSR body pla sta mos8(__rc29) pla sta mos8(__rc28) pla sta mos8(__rc27) pla sta mos8(__rc26) pla sta mos8(__rc25) pla sta mos8(__rc24) pla sta mos8(__rc23) pla sta mos8(__rc22) pla sta mos8(__rc21) pla sta mos8(__rc20) pla sta mos8(__rc19) pla sta mos8(__rc18) pla sta mos8(__rc17) pla sta mos8(__rc16) pla sta mos8(__rc15) pla sta mos8(__rc14) pla sta mos8(__rc13) pla sta mos8(__rc12) pla sta mos8(__rc11) pla sta mos8(__rc10) pla sta mos8(__rc9) pla sta mos8(__rc8) pla sta mos8(__rc7) pla sta mos8(__rc6) pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 4ce6783f606f8f6949f84be3b3a80996c7abee1a File:Hello Commander X16.png 6 52 353 2022-08-12T06:10:16Z Wombat 9 wikitext text/x-wiki Hello Commander X16 from C 49d8af32645c1f070e40c9ad41e380fce36c960e NES overview 0 53 356 2022-08-25T06:35:30Z Mysterymath 3 Start a NES page. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |0 |NROM |<code>nes-nrom</code> |- |1 |MMC1 |<code>nes-mmc1</code> |} == Configuration == As far as is possible, mappers are configured by assigning symbol values. 42098ee16f576a2b1bc5a32ed0601fef259b055c 358 356 2022-08-25T16:15:20Z Mysterymath 3 Soft delete NES overview. wikitext text/x-wiki #REDIRECT [[NES targets]] 692bf0f02fa1b65da68cf37d07827941a940c977 NES targets 0 54 357 2022-08-25T16:11:58Z 2620:0:1000:5011:B4FD:4A41:D8E9:DAF5 0 Copy from "NES overview" wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |0 |NROM |<code>nes-nrom</code> |- |1 |MMC1 |<code>nes-mmc1</code> |} == Configuration == As far as is possible, mappers are configured by assigning symbol values. 42098ee16f576a2b1bc5a32ed0601fef259b055c 359 357 2022-08-25T16:58:44Z Mysterymath 3 Add iNES 2.0 symbols. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |0 |NROM |<code>nes-nrom</code> |- |1 |MMC1 |<code>nes-mmc1</code> |} == Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files provided by the individual target. The semantics of this are given in each individual target section below. 45f4747fdc1bcc596b9c5f8ac7db4a2f10e370f0 360 359 2022-08-25T16:59:20Z Mysterymath 3 Add leading zeros. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |} == Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files provided by the individual target. The semantics of this are given in each individual target section below. 89021eb6fa7cd0c99a9e454c522cb475294a497c 361 360 2022-08-25T17:13:35Z Mysterymath 3 Add mapper descriptions. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |} == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included by default. |- |<code>c-in-ram.ld</code> |Place the C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included by default. |} == [https://www.nesdev.org/wiki/NROM NROM] == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the C sections into PRG-RAM bank 0. |- |<code>prg-ram-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-ram-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-ram-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-ram-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |} 17f13cb9baefa2ecd2b3f497466d95989957a99a 362 361 2022-08-25T17:28:34Z Mysterymath 3 Add section descriptions. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included by default. |- |<code>c-in-ram.ld</code> |Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included by default. |} == [https://www.nesdev.org/wiki/NROM NROM] == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections into PRG-RAM bank 0. |- |<code>prg-ram-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-ram-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-ram-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-ram-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |} 5d37acf9a16cb5052d3ab889212ba751dd62577d 363 362 2022-08-25T17:55:43Z Mysterymath 3 Clarify defaults. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM] == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections into PRG-RAM bank 0. |- |<code>prg-ram-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-ram-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-ram-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-ram-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} 2e58bec8e001b192e2d18451da693740cd6c118e 368 363 2022-10-10T18:53:51Z 71.198.117.145 0 wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections into PRG-RAM bank 0. |- |<code>prg-ram-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-ram-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-ram-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-ram-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} 3cbe4af73ab89f59d489a7ba3f71b908721c457e 370 368 2022-11-04T20:42:23Z Mysterymath 3 Add MMC3 description. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections into PRG-RAM bank 0. |- |<code>prg-ram-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-ram-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-ram-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-ram-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |- |<code>prg-ram-banked-8.ld</code> |One 8KiB bank is visible at 0x8000, and the last 3 banks are fixed contiguously at 0xa000. |- |<code>prg-ram-banked-mode-0.ld</code> |Two independent 8KiB banks at 0x8000 and 0xa000, and the last 2 banks fixed contiguously at 0xc000. |- |<code>prg-ram-banked-mode-1.ld</code> |Two independent 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-ram-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} 67beeab09f3fdb937375b134f5249276aabed119 387 370 2022-12-23T15:03:16Z Mysterymath 3 Fix section naming. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections into PRG-RAM bank 0. |- |<code>prg-ram-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-ram-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-ram-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-ram-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-ram-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |- |<code>prg-ram-banked-8.ld</code> |One 8KiB bank is visible at 0x8000, and the last 3 banks are fixed contiguously at 0xa000. |- |<code>prg-ram-banked-mode-0.ld</code> |Two independent 8KiB banks at 0x8000 and 0xa000, and the last 2 banks fixed contiguously at 0xc000. |- |<code>prg-ram-banked-mode-1.ld</code> |Two independent 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-ram-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} c1be224039cb70b61f4cc26c853a2d12a2685a53 Linker Script 0 45 364 312 2022-08-25T20:27:02Z Mysterymath 3 Describe OVERLAY extension. wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts. See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior. This page describes LLVM-MOS's extensions of LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code> * A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code> Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. === OVERLAY === As an extension, LLVM-MOS allows OVERLAY to be added to regular output section definitions:<syntaxhighlight> .foo : OVERLAY { ... } </syntaxhighlight> [[Category:Linking]] This prevents the linker from checking for overlapping VMAs for any sections marked with OVERLAY. This is more flexible than the existing OVERLAY syntax, which places additional constraints on the layout of the sections that are to receive overlapping VMAs. af308ccee8c4518ed491c8588cdada5a4038bb15 401 364 2023-03-06T19:56:04Z 24.130.71.125 0 Remove OVERLAY extension; no longer needed. wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts. See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior. This page describes LLVM-MOS's extensions of LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code> * A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code> Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. [[Category:Linking]] The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. 63e97fbd508d33b8ef3fabb51e8410406486bfcb Clangd 0 55 365 2022-09-24T21:24:29Z Mysterymath 3 Added documentation for setting up clangd. wikitext text/x-wiki The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK. The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though: # Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory. # Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd. # Set your editor so that the argument <code>--query-compiler="/path/to/llvm-mos-sdk/bin/*clang*"</code> is passed to clangd. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers. == CMake + Visual Studio Code == To set up clangd when building using CMake and editing with Visual Studio Code: # Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd. # Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators. # Build the project. # In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd." # In the setting titled "Clangd: Arguments", add the argument <code>--query-compiler="/path/to/llvm-mos-sdk/bin/*clang*"</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. # Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path. # Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install. df3ac35f07fad3595223eb982036f34b3d21d3a6 366 365 2022-09-25T00:45:45Z Atn34 10 Update clang + vscode instructions to match what worked for me wikitext text/x-wiki The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK. The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though: # Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory. # Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd. # Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers. == CMake + Visual Studio Code == To set up clangd when building using CMake and editing with Visual Studio Code: # Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd. # Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators. # Build the project. # In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd." # In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. # Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path. # Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install. d3ec6ddaf84fc432dfda41e0419d014e12d8ed04 C calling convention 0 17 367 326 2022-10-07T00:47:05Z 107.241.93.203 0 Remove WIPey text. wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 3a214d1d4fab3fd42baee3553ef8bb6745dcd88e 384 367 2022-12-07T00:04:44Z 81.164.105.219 0 fix dead link wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] 547d8327a1ed440e4079fcaa75962a78cba9ec77 Rust 0 50 369 352 2022-10-18T18:02:57Z Wombat 9 Add link to github template for mos-hardware wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. 4a1538395a7110b8429cafd74e5cb0d71ffd97dc 375 369 2022-11-13T19:40:26Z Wombat 9 Add reference to mos-alloc crate wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] provides an allocate needed for heap allocation, ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. ef803e69ab6517bd5d33d039cef3655a908d4b02 376 375 2022-11-13T21:03:16Z Wombat 9 Grammar wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. be7cbdbe2c01f0438076e2bf2b2823e9d994cfe6 385 376 2022-12-13T12:50:32Z Wombat 9 /* Other resources */ wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 1731264790299aedb9e4189c08267ff66f649fd2 386 385 2022-12-13T13:05:58Z Wombat 9 Correct spelling wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 945cff244987a1f2653b1b4141e66ba953c977d1 388 386 2022-12-31T08:33:14Z Wombat 9 Add more docker information wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker from Visual Studio Code ==== By providing a `.devcontainer.json` file, VSC can automatically launch a container for your project. See e.g. `[https://crates.io/crates/mos-hardware mos-hardware]` or other examples at the bottom of this page. ==== Using Docker from the terminal ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for aarch64] (faster on Apple silicon) *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 522dfa042d468a86855138a2bc6ff123115ce3cb 389 388 2022-12-31T08:35:45Z Wombat 9 Docker information wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker from Visual Studio Code ==== By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See e.g. [https://crates.io/crates/mos-hardware mos-hardware] or other examples at the bottom of this page. ==== Using Docker from the terminal ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86 (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon) *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 699eba9f5205d847568493bda1f44c743fbfedf1 390 389 2022-12-31T08:51:31Z Wombat 9 Add arm64 information to docker usage wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker from Visual Studio Code ==== By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See e.g. [https://crates.io/crates/mos-hardware mos-hardware] or other examples at the bottom of this page. ==== Using Docker from the terminal ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. You may replace <code>mrkits/rust-mos</code> with <code>mikaellund/rust-mos</code> for an arm-based build (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon) *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 296a81da53ab2667e06b2a28d5902f777a893e20 391 390 2022-12-31T08:57:48Z Wombat 9 Docker documentation wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker from Visual Studio Code ==== By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See Hello World, mos-hardware, or other examples at the bottom of this page for how to set this up. ==== Using Docker from the terminal ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. You may replace <code>mrkits/rust-mos</code> with <code>mikaellund/rust-mos</code> for an arm-based build (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon) *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 48e24028ef37595c0abd4dbc6512b35b7c146e99 393 391 2023-01-04T21:51:51Z Mrk 6 add mos-test wikitext text/x-wiki The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image]. === Getting started === ==== Using Docker from Visual Studio Code ==== By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See Hello World, mos-hardware, or other examples at the bottom of this page for how to set this up. ==== Using Docker from the terminal ==== Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash"> docker pull mrkits/rust-mos docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos </syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. You may replace <code>mrkits/rust-mos</code> with <code>mikaellund/rust-mos</code> for an arm-based build (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash"> docker container exec -it myrustmos /bin/bash </syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash"> docker container start -i myrustmos </syntaxhighlight> ==== Minimal Example ==== For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code. === Other resources === *[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate]. *[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>. *[https://crates.io/crates/mos-test mos-test] crate providing alternative test harness that lets you write and run tests on all llvm-mos platforms *[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project. *How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon) *Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world]. *Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser. *Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄 0127aa1ba520d0d4e290cbb147dbfc36d9c47778 Optimization guide 0 56 371 2022-11-08T04:04:15Z Mysterymath 3 Added an optimization Guide wikitext text/x-wiki Clang is a modern, optimizing C compiler, and LLVM-MOS's fork of it is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and getting the most out of a modern compiler for embedded development requires some strategy. Some of the cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either. The purpose of this guide is to help the reader gain a simple, but accurate, mental model of what Clang and LLVM-MOS are doing with their code behind the scenes, and how to help that engine to generate the best resulting assembly possible. == Optimizing Compilers == Older optimizing compilers tended to more-or-less function as peephole optimizers: they naively emit a fixed set of instructions for each C construct, then they run optimization passes over the results later, looking for known inoptimal patterns and fixing them up. You can get surprisingly far this way, but fairly quickly you run into a local optimum: there's no small, simple, local transformation that can improve the assembly, but the result is still much, much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better. Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass makes some kind of decision or transformation of the code. Some passes are like the simple transformation passes, that replace one code snippet with another. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serializes as machine code and sent onward to the linker. The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up. == Rules == Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS: * Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly). * If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly calls and C to assembly declarations with <code>__attribute__((leaf))</code>. Otherwise, the compiler is forced to pessimize what those calls could call, which tends to also make it fall back to using a soft stack for the affected functions. * Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). Keeping the values in local variables or expressions instead allow the compiler to worry about the value of the variable at various times in the program. Those values may be able to live in completely different locations. For example, a value may move into A to have some arithmetic done on it, and the result live in X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store. * Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the compiler is required to make their address different from the address of every other pointer in the program. In practice, this means that the array will be copied. You can solve this by either making the array static, or making it global. * Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you asked for. * Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also somewhat reduce the compiler's ability to do so, so you may actually get slightly worse code for the extra effort, too. * Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here. * Prefer array indices of <256. For obvious reasons. LLVM-MOS comes equipped with the full gamut of addressing modes in it's instruction selector, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them. [[Category:C]] [[Category:Code generation]] e5ef4f2e94585b35f4052aab2888d8aa6fa82089 372 371 2022-11-08T04:29:29Z Mysterymath 3 /* Rules */ wikitext text/x-wiki Clang is a modern, optimizing C compiler, and LLVM-MOS's fork of it is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and getting the most out of a modern compiler for embedded development requires some strategy. Some of the cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either. The purpose of this guide is to help the reader gain a simple, but accurate, mental model of what Clang and LLVM-MOS are doing with their code behind the scenes, and how to help that engine to generate the best resulting assembly possible. == Optimizing Compilers == Older optimizing compilers tended to more-or-less function as peephole optimizers: they naively emit a fixed set of instructions for each C construct, then they run optimization passes over the results later, looking for known inoptimal patterns and fixing them up. You can get surprisingly far this way, but fairly quickly you run into a local optimum: there's no small, simple, local transformation that can improve the assembly, but the result is still much, much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better. Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass makes some kind of decision or transformation of the code. Some passes are like the simple transformation passes, that replace one code snippet with another. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serializes as machine code and sent onward to the linker. The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up. == Rules == Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS: * Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly). * If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly calls and C to assembly declarations with <code>__attribute__((leaf))</code>. Otherwise, the compiler is forced to pessimize what those calls could call, which tends to also make it fall back to using a soft stack for the affected functions. * Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). Keeping the values in local variables or expressions instead allow the compiler to worry about the value of the variable at various times in the program. Those values may be able to live in completely different locations. For example, a value may move into A to have some arithmetic done on it, and the result live in X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store. * Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global. * Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you asked for. * Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also somewhat reduce the compiler's ability to do so, so you may actually get slightly worse code for the extra effort, too. * Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here. * Prefer array indices of <256. For obvious reasons. LLVM-MOS comes equipped with the full gamut of addressing modes in it's instruction selector, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them. [[Category:C]] [[Category:Code generation]] 220dea85fefa79f064b4a1e1ce15b6ea9a2c105e 373 372 2022-11-09T05:31:03Z Mysterymath 3 Copy edit the optimization guide. wikitext text/x-wiki Clang is a modern, optimizing C compiler, and LLVM-MOS's fork is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and it takes some strategy to get the most out of a modern compiler on embedded platforms. Some of the standard cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either. The purpose of this guide is to help the reader build a simple, but accurate, mental model of how Clang and LLVM-MOS work and how to help them generate better code. == Optimizing Compilers == If they had optimization at all, older compilers tended to exclusively use "peephole optimization". They would emit a fixed assembly pattern for each C construct. Afterwards, they would run optimization passes over the results later, looking for known poor assembly patterns and replacing them with better ones. You can get surprisingly far this way, but fairly quickly you run into a local optimum. Eventually, there won't be a small, simple, local transformation that can improve the assembly, but the result is still much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better. Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass either analyzes the code or transforms it. Some passes are simple peephole passes. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serialized to machine code and sent onward to the linker. The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up. == Rules == Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS: * Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly). * If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly and assembly function declarations in C with <code>__attribute__((leaf))</code>. Otherwise, the compiler must pessimize what those calls could in turn call, which tends to also make it fall back to using a soft stack for the affected functions. * Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). On the other hand, a local variable or expression may live in completely different locations as the program progresses. For example, a variable may move into A to have some arithmetic done on it, and the result move to X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store. * Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global. * Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you've asked for. * Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also reduce the compiler's ability to do so, so you may get worse code for your effort, too. * Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here. * Prefer array indices of <256, for obvious reasons. LLVM-MOS can select the full gamut of addressing modes, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them. [[Category:C]] [[Category:Code generation]] c9944ebb248d42f6609176a6029f10e5ad0f7746 374 373 2022-11-09T05:32:19Z Mysterymath 3 Remove "for obvious reasons." wikitext text/x-wiki Clang is a modern, optimizing C compiler, and LLVM-MOS's fork is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and it takes some strategy to get the most out of a modern compiler on embedded platforms. Some of the standard cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either. The purpose of this guide is to help the reader build a simple, but accurate, mental model of how Clang and LLVM-MOS work and how to help them generate better code. == Optimizing Compilers == If they had optimization at all, older compilers tended to exclusively use "peephole optimization". They would emit a fixed assembly pattern for each C construct. Afterwards, they would run optimization passes over the results later, looking for known poor assembly patterns and replacing them with better ones. You can get surprisingly far this way, but fairly quickly you run into a local optimum. Eventually, there won't be a small, simple, local transformation that can improve the assembly, but the result is still much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better. Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass either analyzes the code or transforms it. Some passes are simple peephole passes. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serialized to machine code and sent onward to the linker. The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up. == Rules == Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS: * Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly). * If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly and assembly function declarations in C with <code>__attribute__((leaf))</code>. Otherwise, the compiler must pessimize what those calls could in turn call, which tends to also make it fall back to using a soft stack for the affected functions. * Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). On the other hand, a local variable or expression may live in completely different locations as the program progresses. For example, a variable may move into A to have some arithmetic done on it, and the result move to X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store. * Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global. * Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you've asked for. * Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also reduce the compiler's ability to do so, so you may get worse code for your effort, too. * Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here. * Prefer array indices of <256. LLVM-MOS can select the full gamut of addressing modes, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them. [[Category:C]] [[Category:Code generation]] 4eb95605cc482e8d100d6ed26c99052cf3d13d7a ELF specification 0 15 377 298 2022-11-22T19:53:00Z 209.79.16.69 0 Fixed the "no relocation type" entry wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.1.4 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |14 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [[Category:Linking]] d5355a67441b5c723ff7db1cf918f442f16886c4 403 377 2023-06-12T18:40:17Z Asie 12 Update to 0.2.1 in accordance with the discussions in PR #296. wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.2.1 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |14 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [[Category:Linking]] a361e8915645fdfe04a9033e9efb009bbcc0726f C Inline Assembly 0 40 378 340 2022-11-30T03:41:58Z Mysterymath 3 wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. [[Category:C]] 7c2585c8ba335f630466ee7bc965c46ae3273c8b Modifiers 0 16 379 325 2022-11-30T07:09:57Z Mysterymath 3 Add zeropage directive. wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte [[/cdn-cgi/l/email-protection|[email protected]]]</code> (or similar). The directive <code>.zeropage <global> [, ...]</code> allows marking an external symbol as being in the zero page. Without this, each use would need to be annotated as <code>mos8</code>, or it would be conservatively relaxed to a 16-bit address. [[Category:Assembly]] 5ba774dc9d8e3496470aa6c27e3fd17613f2a37e 380 379 2022-11-30T07:11:53Z Mysterymath 3 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte [[/cdn-cgi/l/email-protection|[email protected]]]</code> (or similar). [[Category:Assembly]] ffe274b4a656bfb879c622ae292dc4af0a1081da Assembler relaxation 0 19 381 330 2022-11-30T07:12:33Z Mysterymath 3 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. # the symbol was marked with the <code>.zeropage <symbol></code> directive. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section .zp,"a",@nobits low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"az",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] 3f8794205b585b0d3789e94cf982b6c70a08f799 382 381 2022-11-30T07:13:40Z Mysterymath 3 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. # the symbol was marked with the <code>.zeropage <symbol></code> directive. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section .zp,"a",@nobits low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"az",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. Finally, if the symbol is undefined, the directive <code>.zeropage <symbol></code> can mark it as being in the zero page. It is an error to use this on a symbol defined in the current file; the determination for such symbols is made via the above mechanisms. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] 42777a2f67df49775694bcd88eea461bae149bf4 383 382 2022-11-30T07:20:07Z Mysterymath 3 wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag. # the symbol was marked with the <code>.zeropage <symbol></code> directive. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section .zp,"a",@nobits low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"az",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. Finally, the directive <code>.zeropage <symbol></code> can mark it as being in the zero page. This is most useful for undefined references, since it does nothing to actually place the symbol in the zero page; it just makes references to the symbol use 8-bit addressing modes. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] 55f0eba61913f8b45f37e9252e03a54caacd3838 398 383 2023-02-17T23:39:06Z 209.79.16.69 0 Added an or. wikitext text/x-wiki The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hello,x In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address. In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in. LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over. This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain. The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page. A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if: # the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or # the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or # the symbol is defined in a section marked with the special <code>z</code> flag, or # the symbol was marked with the <code>.zeropage <symbol></code> directive. If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption. So, one way to force a zero page access is fairly straightforward: low_addr = 55 + 2 * 4 lda low_addr,x Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you. Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections: .section .zp,"a",@nobits low_short: .byte 0x00 0x00 .section text high_short: .byte 0x01 0x00 lda low_short,x A third way to force a zero page access is to mark the section with the special `z` flag: .section .lowmemory,"az",@nobits adrlowmemory: .ds.b 1 Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte. This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory. Finally, the directive <code>.zeropage <symbol></code> can mark it as being in the zero page. This is most useful for undefined references, since it does nothing to actually place the symbol in the zero page; it just makes references to the symbol use 8-bit addressing modes. If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution. [[Category:Assembly]] 7f817bbe7c2530e30b9582af82b747e5ba547fab Findings 0 43 392 293 2023-01-03T19:03:31Z 2A01:586:A8F5:1:AB2:C42E:9A1B:6C19 0 wikitext text/x-wiki == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. === Stacks === Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behave in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. === Registers === The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == ELF == Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. == GAS Assembler == The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments. == Non-C languages == LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. [[Category:Main]] 066168e60e712ab2a83596fbc1280e8f4f5f40a1 394 392 2023-01-07T12:51:52Z 180.150.37.168 0 /* ELF */ Fix a typo. wikitext text/x-wiki == Advantages == With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer. Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges. There are two main obstacles when turning C into efficient machine code: stacks and registers. === Stacks === Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming. While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behave in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM. === Registers === The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload. The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''. Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve. Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained. == ELF == Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can perform detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization. == GAS Assembler == The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments. == Non-C languages == LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code. == Challenges == LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis. Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants. [[Category:Main]] 9aa0a2cda59385c02bc14d4a0235589bf632965f Getting started 0 23 395 305 2023-01-07T13:11:41Z 180.150.37.168 0 Fixed a spelling error. wikitext text/x-wiki See [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting Started with the SDK]. [[Category:Main]] 6a3d62fb01ca7687d857a3420666b591a6291d6b Welcome 0 1 396 355 2023-01-23T23:03:58Z Jbyrd 1 Changed to Discord invite wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} b7e1e5b4ff309d97eac47a82d55e27931468fbad 397 396 2023-02-02T20:00:32Z Jbyrd 1 Discord, not Slack. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 79b55cfc19fbf3d8bc9229f438ff153ec5dd7e83 400 397 2023-02-21T01:04:52Z Jbyrd 1 Linked NES wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} c9ce97dc6c9b9437472d15a21d202100226a10b1 File:LLVM-MOS logo.png 6 57 399 2023-02-21T01:02:32Z Jbyrd 1 wikitext text/x-wiki 8-bit LLVM dragon logo 856d56c8ecb58ddafb7e2d5d794efe1bf12ef1d8 Code generation overview 0 22 402 328 2023-05-08T20:41:25Z 89.134.5.94 0 fix typo wikitext text/x-wiki See also [https://www.youtube.com/watch?v=2lW3WHPtmKo this talk at 2022 EuroLLVM], which gives a high-level overview of llvm-mos's code generation strategy. Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection. The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR". To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking. Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it. The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware. Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a small handful of 6502 instructions. This is a big win when operating on pointed-to structs. We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and an 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization. [[Category:Code generation]] 4ad7e05b585b428b3e9c65ea363bd11f2ec7fd89 Contributing 0 58 404 2023-06-13T00:57:06Z Mysterymath 3 Added a contributing page. wikitext text/x-wiki llvm-mos is a project with considerable scope, and considerably less resources. We can always use more help! ==SDK== The [http://SDK https://github.com/llvm-mos/llvm-mos-sdk] contains the general C and C++ libraries and linker scripts that provide support for each platform. If you have knowledge of a 6502 system and are willing to monkey around with a GCC-alike compiler, then you could help by adding support for a new platform, extending the C library coverage for that platform's OS or hardware, or by adding new configurations of platforms (i.e., tape, boot disk, baremetal, SD card boot, etc.) The [[Porting]] guide describes how to bring up a new target from scratch using the base compiler and <code>common</code> target, while the [[Extending SDK]] guide describes how to incorporate such changes into the SDK's CMake scripts. Reading these also provides a good baseline for understanding the layout of the SDK. Afterwards, just jump in and start picking apart how your (or the most similar to your) target works, and feel free to ask questions in the [https://discord.gg/D9gRm3aznh Discord]. ==Compiler== Contributing to the compiler is considerably less straightforward. (But it's still greatly appreciated!) LLVM is one of the world's premier industrial-duty compiler frameworks; it's tremendously powerful, but with great power (usually) comes great complexity, and LLVM is no exception. Learning how it works can be easier or harder, depending on what exactly it is you're trying to do, as you may have to only learn a tiny portion of its codebase, or you may need to make sweeping changes throughout. A great resource for this is https://godbolt.org; this allows you to analyze any snippet of C or C++ in the llvm-mos compiler. Adding a "LLVM Opt Pipeline" view to the compiler in Godbolt shows the program as each pass gradually transform it from almost-C all the way to 6502 assembly. This is a great way to learn the breakdown of which responsibilities lie with which LLVM passes, and where one should go looking to make changes. After that, it's mostly a matter of reading code, reading comments, and playing with toy examples until you've built a sufficient mental model of how stuff works to make changes. There are a few resources available to help kickstart the mental-model building process. First, there's an excellent [https://www.youtube.com/watch?v=objxlZg01D0 video overview] of how LLVM backends work; this is probably the best bang-for-your buck in learning LLVM's backend architecture. Second, there's a [https://www.youtube.com/watch?v=McByO0QgqCY video overview] and readable [https://llvm.org/docs/GlobalISel/index.html reference documentation] for how the Global Instruction Selector (GlobalISel) works; this is the process of lowering abstract instructions like "multiply" into moderately 6502-ey pseudo-instructions, and it's accordingly where most of the llvm-mos backend actually operates. There's also a variety of llvm-mos-specific passes; these implement logic that doesn't fit anywhere else in LLVM's existing framework. Lastly, don't struggle alone (like I did); ask questions on our Discord instead! There's quite a bit of LLVM expertise floating around there at this point, and we might be able to save a lot of time on your end with just a little of ours. 4bffc9f65b07db6650297e97dded6179ed5e2434 405 404 2023-06-13T00:58:49Z Jbyrd 1 Linked to Discord wikitext text/x-wiki llvm-mos is a project with considerable scope, and considerably less resources. We can always use more help! ==SDK== The [http://SDK https://github.com/llvm-mos/llvm-mos-sdk] contains the general C and C++ libraries and linker scripts that provide support for each platform. If you have knowledge of a 6502 system and are willing to monkey around with a GCC-alike compiler, then you could help by adding support for a new platform, extending the C library coverage for that platform's OS or hardware, or by adding new configurations of platforms (i.e., tape, boot disk, baremetal, SD card boot, etc.) The [[Porting]] guide describes how to bring up a new target from scratch using the base compiler and <code>common</code> target, while the [[Extending SDK]] guide describes how to incorporate such changes into the SDK's CMake scripts. Reading these also provides a good baseline for understanding the layout of the SDK. Afterwards, just jump in and start picking apart how your (or the most similar to your) target works, and feel free to ask questions in the [https://discord.gg/D9gRm3aznh Discord]. ==Compiler== Contributing to the compiler is considerably less straightforward. (But it's still greatly appreciated!) LLVM is one of the world's premier industrial-duty compiler frameworks; it's tremendously powerful, but with great power (usually) comes great complexity, and LLVM is no exception. Learning how it works can be easier or harder, depending on what exactly it is you're trying to do, as you may have to only learn a tiny portion of its codebase, or you may need to make sweeping changes throughout. A great resource for this is https://godbolt.org; this allows you to analyze any snippet of C or C++ in the llvm-mos compiler. Adding a "LLVM Opt Pipeline" view to the compiler in Godbolt shows the program as each pass gradually transform it from almost-C all the way to 6502 assembly. This is a great way to learn the breakdown of which responsibilities lie with which LLVM passes, and where one should go looking to make changes. After that, it's mostly a matter of reading code, reading comments, and playing with toy examples until you've built a sufficient mental model of how stuff works to make changes. There are a few resources available to help kickstart the mental-model building process. First, there's an excellent [https://www.youtube.com/watch?v=objxlZg01D0 video overview] of how LLVM backends work; this is probably the best bang-for-your buck in learning LLVM's backend architecture. Second, there's a [https://www.youtube.com/watch?v=McByO0QgqCY video overview] and readable [https://llvm.org/docs/GlobalISel/index.html reference documentation] for how the Global Instruction Selector (GlobalISel) works; this is the process of lowering abstract instructions like "multiply" into moderately 6502-ey pseudo-instructions, and it's accordingly where most of the llvm-mos backend actually operates. There's also a variety of llvm-mos-specific passes; these implement logic that doesn't fit anywhere else in LLVM's existing framework. Lastly, don't struggle alone (like I did); ask questions on our [https://discord.gg/D9gRm3aznh Discord] instead! There's quite a bit of LLVM expertise floating around there at this point, and we might be able to save a lot of time on your end with just a little of ours. 7878492954364fba8e70738dd2ce42614988e3e9 Optimization guide 0 56 406 374 2023-06-18T20:54:38Z 2600:387:6:807:0:0:0:2C 0 Add optimization flags wikitext text/x-wiki Clang is a modern, optimizing C compiler, and LLVM-MOS's fork is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and it takes some strategy to get the most out of a modern compiler on embedded platforms. Some of the standard cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either. The purpose of this guide is to help the reader build a simple, but accurate, mental model of how Clang and LLVM-MOS work and how to help them generate better code. == Optimizing Compilers == If they had optimization at all, older compilers tended to exclusively use "peephole optimization". They would emit a fixed assembly pattern for each C construct. Afterwards, they would run optimization passes over the results later, looking for known poor assembly patterns and replacing them with better ones. You can get surprisingly far this way, but fairly quickly you run into a local optimum. Eventually, there won't be a small, simple, local transformation that can improve the assembly, but the result is still much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better. Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass either analyzes the code or transforms it. Some passes are simple peephole passes. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serialized to machine code and sent onward to the linker. The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up. == Rules == Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS: * Use <code>-Os</code> and <code>-flto</code>. These are the defaults for all targets, but they aren't automatically applied when compiling code snippets on the command line. LLVM-MOS seems to produce much better code overall when optimizing for size than for speed, and LTO enables a whole host of whole-program optimizations vital to getting good 6502 codegen. * Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly). * If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly and assembly function declarations in C with <code>__attribute__((leaf))</code>. Otherwise, the compiler must pessimize what those calls could in turn call, which tends to also make it fall back to using a soft stack for the affected functions. * Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). On the other hand, a local variable or expression may live in completely different locations as the program progresses. For example, a variable may move into A to have some arithmetic done on it, and the result move to X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store. * Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global. * Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you've asked for. * Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also reduce the compiler's ability to do so, so you may get worse code for your effort, too. * Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here. * Prefer array indices of <256. LLVM-MOS can select the full gamut of addressing modes, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them. [[Category:C]] [[Category:Code generation]] f3b785cfcece15b2e246bc554e6dc42052ed2e4a PCE target 0 59 407 2023-06-21T14:28:40Z Asie 12 Created page with "VThe PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with addi..." wikitext text/x-wiki VThe PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF |User |Controlled by the software developer |- |3 |$6000 - $7FFF |User | |- |4 |$8000 - $9FFF |User | |- |5 |$A000 - $BFFF |User | |- |6 |$C000 - $DFFF |User | |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #* <code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code; it must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with nothing inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. 88263a90583c3fbc58976ddb1335e2150ab2dc07 408 407 2023-06-21T14:29:37Z Asie 12 add pull request information wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF |User |Controlled by the software developer |- |3 |$6000 - $7FFF |User | |- |4 |$8000 - $9FFF |User | |- |5 |$A000 - $BFFF |User | |- |6 |$C000 - $DFFF |User | |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #* <code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code; it must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with nothing inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. c4075e522beb2e16dd9f6f5e43705571705dbee8 409 408 2023-06-21T14:31:48Z Asie 12 minor visual improvements wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #* <code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code; it must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with nothing inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. a10c16d724d06f25e489c99ea2fd7ae58b4c4798 410 409 2023-06-21T14:45:21Z Asie 12 minor naming scheme change, clarification wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. 8e80b02c3e1bec3c316da1bc0f137b199a94852a 411 410 2023-06-21T14:52:41Z Asie 12 "defining" => "using macros" wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. 865b40594a8b3d3b00d7017bd9616c2511a6e9cf 412 411 2023-06-21T15:45:55Z Asie 12 document LMA->physical bank/address conversion, minor fixes wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. 043406b3ba62cf45567359a78c69f88da5da5dff 415 412 2023-06-21T20:33:53Z Asie 12 explain fixed bank storage in ROM wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == TODO: Document (should work roughly the same as on the NES targets). == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. b7e03ad0ade817826aa70ee932b6cf210f9c4348 416 415 2023-06-21T20:37:21Z Asie 12 briefly document interrupts wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. cc2c39f1ffa00f7f5858b084b30d83c78b07e576 417 416 2023-06-21T20:39:51Z Asie 12 add note on SuperGrafx support wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. 7ce30d93feeddf59155dfbb842a0816c9f8fc08a 418 417 2023-06-21T20:41:53Z Asie 12 explain .mlb files wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. e031380ac4f3a6f458c4cb21864070b0a73bacf5 419 418 2023-06-21T20:42:44Z Asie 12 document bank index 0 unmapping in the context of interrupts wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. f1aba43ec9d2658147fd27504d3dac8c3d87bc24 420 419 2023-06-22T18:58:55Z Asie 12 document pce_rom_vbankN_bank() wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>uint8_t pce_rom_vbankN_bank()</code> - returns the first physical bank ID ($00 - $7F) of VBank N; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage - matches <code>pce_rom_vbankN_bank()</code>'s output. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>PhysBank = (LMA - BankLMA) >> 13</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. 1274294dcb4080c06c4ba35f57bcf50de9c2ae56 421 420 2023-06-22T21:05:06Z Asie 12 fix PhysBank equation wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !Index !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Virtual banks === As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes. A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>. #*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween. #* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space. #* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6. #* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space; * <code>uint8_t pce_rom_vbankN_bank()</code> - returns the first physical bank ID ($00 - $7F) of VBank N; * <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it; * the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to; * the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage - matches <code>pce_rom_vbankN_bank()</code>'s output. Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. 6b84c14d800e213cc5a6129f69f89b38dd490c51 422 421 2023-06-25T12:52:35Z Asie 12 replace virtual bank section with a description of what was actually merged wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. '''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121]. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. b272a758bf57b260365c7015b5a5a41719fcb411 423 422 2023-06-25T12:52:50Z Asie 12 remove non-upstreamed notice, as this target was upstreamed wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. * '''Q:''' Is the PC Engine CD supported? * '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future. * '''Q:''' Is the SuperGrafx supported? * '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. e24fcf3f92944aaabdf976c401ea9ee35d5d044e 424 423 2023-06-30T16:44:58Z Asie 12 initial pce-cd docs wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. d2663381c9db4031feb45bf217901f1db58618f5 425 424 2023-06-30T16:51:22Z Asie 12 document pce-mkcd ipl.bin requirement wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in the root working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. b893c536db8866d7ef793a03ef056241ccd8f647 426 425 2023-06-30T16:51:40Z Asie 12 wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc. == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. dbb9a36e035a67334d9068f7ef5f6b3a9a6b3bb8 427 426 2023-06-30T18:26:03Z Asie 12 document .cue file wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc. To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below:<syntaxhighlight> FILE output.iso BINARY TRACK 01 MODE1/2048 INDEX 01 00:00:00 FILE output.wav WAV TRACK 02 AUDIO INDEX 01 00:00:00 </syntaxhighlight> == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. 4a861e37041bb4d0c86adff7f3fd491f0e50e704 428 427 2023-06-30T18:26:56Z Asie 12 formatting fix wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc. To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below: <code> FILE output.iso BINARY TRACK 01 MODE1/2048 INDEX 01 00:00:00 FILE output.wav WAV TRACK 02 AUDIO INDEX 01 00:00:00 </code> == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. 175fff6882aa44a323166dafda42c6706e33cef0 429 428 2023-06-30T18:27:25Z Asie 12 formatting fix 2 wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc. To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below: FILE output.iso BINARY TRACK 01 MODE1/2048 INDEX 01 00:00:00 FILE output.wav WAV TRACK 02 AUDIO INDEX 01 00:00:00 == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. 9fd3b97478b99cd741628e0a417f2d634ae8c3b9 430 429 2023-07-01T05:18:34Z Asie 12 document pce-cd limitations wikitext text/x-wiki The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz ** Redirected zero page at 0x2000, stack at 0x2100 ** Interrupt control and timer ** Joypad port input/output * Memory: ** 21-bit address space (8-bit bank index, 8 kilobytes per bank) ** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards ** Up to 1 megabyte of ROM, banks $00-$7F ** Memory mapped I/O, bank $FF * VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors) * PSG: Programmable Sound Generator == Banking == The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows: {| class="wikitable" |+PCE target - Address space !MPR !Addresses !Bank !Description |- |0 |$0000 - $1FFF |$FF |Memory mapped I/O |- |1 |$2000 - $3FFF |$F8 |PC Engine RAM |- |2 |$4000 - $5FFF | rowspan="5" |User | rowspan="5" |Controlled by the software developer |- |3 |$6000 - $7FFF |- |4 |$8000 - $9FFF |- |5 |$A000 - $BFFF |- |6 |$C000 - $DFFF |- |7 |$E000 - $FFFF |$00 |Fixed bank; contains IRQ/reset vectors: * 0xFFF6: IRQ2 (external) * 0xFFF8: IRQ1 (VDC) * 0xFFFA: IRQ0 (timer) * 0xFFFC: NMI (not used) * 0xFFFE: RESET |} === Bank mapping === Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code: # Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>). # Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>. #*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later. #* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive. #* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF. # Create an implementation file (for example, <code>bank.c</code>). Write the following lines: #*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include, #* <code>#include <pce.h></code> #* <code>#include "bank.h"</code> # Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks. A defined virtual bank provides the following convenience methods: * <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space; *<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it; * the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>. === Fixed area === Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible. Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed. Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB. === SuperGrafx RAM === The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8. Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM. == Interrupts == Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined: * <code>irq_external</code> - IRQ2 (external interrupt), * <code>irq_vdc</code> - IRQ1 (VDC), * <code>irq_timer</code> - IRQ0 (timer), * <code>nmi</code> - NMI (not emitted on PC Engine). Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page. == Linker details == === ELF address space === {| class="wikitable" |+PCE target - LMA format !31-24 (Group) ! !23-16 (Bank) ! !15-0 (VMA) |- | rowspan="5" |0x00 | rowspan="5" |Base address space |0x00 .. 0x7F |HuCard ROM | rowspan="9" |6502 address |- |0xF7 |Backup RAM (PC Engine CD) |- |0xF8 |RAM |- |0xF9 .. 0xFB |RAM (SuperGrafx) |- |0xFF |Memory mapped I/O |- | rowspan="3" |0x01 | rowspan="3" |Additional RAM |0x40 .. 0x5F |RAM ("Populous") |- |0x68 .. 0x7F |RAM (Super System Card) |- |0x80 .. 0x87 |RAM (PC Engine CD) |- |0x02 .. 0x11 |"SF2" mapper (Additional ROM banks) |0x40 .. 0x7F |HuCard ROM |} Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation: <code>FirstBank = ((LMA >> 16) & 0xFF)</code> <code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code> <code>PhysAddress = (LMA & 0x1FFF)</code> where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>. == PC Engine CD == PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below. === Additional functionality === The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>. The <code>pce-cd</code> target does not support resizing the fixed bank or mapping SuperGrafx RAM by default. === Banking === On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different: * PC Engine CD, internal RAM: bank indexes 128 - 135 * Super System Card, additional RAM: bank indexes 104 - 127 The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default. The first RAM bank used is expected to be fixed at $4000. === Linking === There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>: * <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM); * <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM); * <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default; * <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector). "Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image. One can also link to contents of other files contained on a CD - see the below section for more information. === Building an ISO === To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program. The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files: * <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file, * <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors, * <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file, * <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file, * <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file, * <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file. In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames. Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc. To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below: FILE output.iso BINARY TRACK 01 MODE1/2048 INDEX 01 00:00:00 FILE output.wav WAV TRACK 02 AUDIO INDEX 01 00:00:00 == FAQ == * '''Q:''' Is the "SF2" mapper supported? * '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration. *'''Q:''' Is the SuperGrafx supported? *'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support). * '''Q:''' Can I safely remap non-User bank indexes? * '''A:''' It depends: ** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it. ** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack. ** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it. * '''Q:''' What is the ".mlb" file generated alongside the ROM output? * '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger. 74adfc8d66cf6cfe88addbbaaa2795e5e5629690 Linker Script 0 45 413 401 2023-06-21T18:21:29Z Asie 12 document FULL/TRIM start, length; minor cleanup wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. For a general reference to ld-style linker scripts, see [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual]. For LLD's extensions and interpretations of ld's behavior, see LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy]. This page describes LLVM-MOS's extensions to LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>. Byte commands output the value of the given expression as little-endian bytes of the size corresponding to the command used. They have the same syntax and semantics as those which occur in output section descriptions. * A memory region command: <code>FULL(''mem-region'', ''[start, [length]]'')</code>, <code>TRIM(''mem-region, [start, [length]]'')</code>. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with an LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The optional <code>start</code> argument allows specifying the starting position, in bytes, of the output section's data to be emitted. Note that this argument is relative to the beginning of the region, and not an absolute LMA. The optional <code>length</code> argument allows specifying the maximum number of bytes to be output as part of this memory region command. Note that if the section has fewer than <code>length</code> bytes remaining, the output won't be padded, but rather fewer bytes will be written. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. [[Category:Linking]] 7383afa576b53da3222f49f79457e69d7193063f 414 413 2023-06-21T18:22:43Z Asie 12 fix formatting wikitext text/x-wiki The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format. For a general reference to ld-style linker scripts, see [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual]. For LLD's extensions and interpretations of ld's behavior, see LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy]. This page describes LLVM-MOS's extensions to LLD's behavior. ===Custom Output Formats=== LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>. The format of the extension is: OUTPUT_FORMAT { ''output-format-command'' ''output-format-command'' ... } The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file. Each ''output-format-comman''d can be: * A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>. Byte commands output the value of the given expression as little-endian bytes of the size corresponding to the command used. They have the same syntax and semantics as those which occur in output section descriptions. * A memory region command: <code>FULL(''mem-region'', ''[start, [length]]'')</code>, <code>TRIM(''mem-region, [start, [length]]'')</code>. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with an LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The optional <code>start</code> argument allows specifying the starting position, in bytes, of the output section's data to be emitted. Note that this argument is relative to the beginning of the region, and not an absolute LMA. The optional <code>length</code> argument allows specifying the maximum number of bytes to be output as part of this memory region command. Note that if the section has fewer than <code>length</code> bytes remaining, the output won't be padded, but rather fewer bytes will be written. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used. [[Category:Linking]] 9d9ea0a461ccc1c8ab14d4325c97670d91bf4a52 ELF specification 0 15 431 403 2023-07-07T17:55:34Z Asie 12 Add R_MOS_IMM16 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.2.2 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |14 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |15 |A 16-bit immediate value. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [[Category:Linking]] f06df052fd9a75c95c8bb905db9b73e5ce1b1516 434 431 2023-08-10T20:48:40Z Asie 12 0.2.3: add 45GS02 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.2.3 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_FK_DATA_4 |12 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |13 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |14 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |15 |A 16-bit immediate value. |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [[Category:Linking]] 668169396e63f7f05f21f9efc44304b5c788eeca C calling convention 0 17 432 384 2023-07-26T21:45:02Z 2620:15C:B7:2:B139:5B1F:F7B6:D926 0 Clarify return value handling. wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] [[Category:C]] e159a6799f3a56e48666d0f73e10a981cec044a5 454 432 2023-09-02T09:35:19Z Wombat 9 Add table with examples illustrating calling conventions wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the (imaginary) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment |- |<code>char f(int a)</code>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |- |<code>int * f(void *a)</code>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2</code>, <code>rc3</code> |- |<code>long f(long a, int b)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code> |- |<code>int f(int a, int b, void *c)</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |- |<code>int f(void *a, char b, int c)</code>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |- |<code>void f(void *a, uint32_t b)</code>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| |- |<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div div_t] a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4 |- |<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div ldiv_t] a)</code>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4 |- |<code>void f(uint64_t a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||By value (incl. <code>rc6-7</code>) |} 140ebadc918ea13e08e36719fe8c63d77a6c9731 455 454 2023-09-02T09:47:53Z Wombat 9 Add link to imaginary registers wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment |- |<code>char f(int a)</code>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |- |<code>int * f(void *a)</code>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2</code>, <code>rc3</code> |- |<code>long f(long a, int b)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code> |- |<code>int f(int a, int b, void *c)</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |- |<code>int f(void *a, char b, int c)</code>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |- |<code>void f(void *a, uint32_t b)</code>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| |- |<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div div_t] a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4 |- |<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div ldiv_t] a)</code>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4 |- |<code>void f(uint64_t a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||By value (incl. <code>rc6-7</code>) |} fc422945f925752fcf73cbba187a04db5f3a7c71 Frequently asked questions 0 49 433 342 2023-08-07T06:06:42Z Wombat 9 /* Rule 2: Inputs, outputs, and volatile. */ wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234"::"R"(value)); } </syntaxhighlight>The <code>R</code> constraint on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. [[Category:Main]] 30f443ab8126e917a4f3aba6f339cf74efd2a70f Zero page 0 60 435 2023-08-21T16:53:24Z Mysterymath 3 Add a page on zero page management wikitext text/x-wiki The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. 13b8179881d2f30338ecf1bc2c3456629ae99665 436 435 2023-08-21T16:53:55Z Mysterymath 3 Add categories wikitext text/x-wiki The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. [[Category:Code generation]] [[Category:C]] f27e78f9d8982ee44936a6c25c984baf34bfaa55 437 436 2023-08-22T21:32:21Z Jbyrd 1 wikitext text/x-wiki In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[calling convention]], and let the compiler manage which variables are stored in zero page when. However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. It is not recommended to "convince" the compiler to try to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job. [[Category:Code generation]] [[Category:C]] 654eeaed40ccc065bb80368be3db9a59e2c9d1b9 438 437 2023-08-22T21:34:46Z Jbyrd 1 wikitext text/x-wiki In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]], and let the compiler manage which variables are stored in zero page when. However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. It is not recommended to "convince" the compiler to try to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job. [[Category:Code generation]] [[Category:C]] a62b8d72696f2521ed54f2ace42c3263992d52da 439 438 2023-08-22T21:37:44Z Jbyrd 1 wikitext text/x-wiki In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]], and let the compiler manage which variables are stored in zero page when. However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. It is not recommended to "convince" the compiler to try to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job. [[Category:Code generation]] [[Category:C]] 93b6dc32127e13b562ceff13751402a0ca9272e5 440 439 2023-08-22T21:39:16Z Jbyrd 1 wikitext text/x-wiki In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]], and let the compiler manage which variables are stored in zero page when. However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. It is not recommended to "convince" the compiler to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job. [[Category:Code generation]] [[Category:C]] 0608591da0a03fb0fa13a5d4eb97cbf9f9224e02 441 440 2023-08-22T23:52:59Z Mysterymath 3 Give more specific reasons why hand-annotation isn't usually necessary wikitext text/x-wiki The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may require the compiler to set aside zero page memory. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]]. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program. [[Category:Code generation]] [[Category:C]] fe4f09560cb24f6126bf26d0e6198ddd72438e91 442 441 2023-08-22T23:53:49Z Mysterymath 3 Minor wording wikitext text/x-wiki The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may reserving zero page memory from the compiler. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]]. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler. While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program. [[Category:Code generation]] [[Category:C]] 14548ad0ecd5e71dd8396c88b7bbc3ae79028635 File:C64-linux-boot-anim.gif.gif 6 61 443 2023-08-26T21:53:45Z Jbyrd 1 wikitext text/x-wiki Onno Kortman's time-lapsed demo of booting Linux on a RISCV emulator on a C64 with REU 405e17ac35e2b0fe0512afc414a79ea2789679e3 Welcome 0 1 444 400 2023-08-26T21:54:41Z Jbyrd 1 Added Linux gif demo wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] [[File:C64-linux-boot-anim.gif.gif|thumb|Onno Kortman's demo of Linux running in time lapse on a RISCV emulator on a C64 with REU]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 768210cebf665b30fed23c86744b43cf2ca93280 445 444 2023-08-26T21:55:23Z Jbyrd 1 Onno Kortman's Linux gif does not animate on home page, so deleted. wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} c9ce97dc6c9b9437472d15a21d202100226a10b1 450 445 2023-09-01T16:40:33Z Mysterymath 3 Update wording wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Discord. If you'd like to help out, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 212dd9fd3afa410e7c1427ccf1c72760a408a1c4 NES targets 0 54 446 387 2023-08-27T22:35:16Z Cogwheel 13 /* INCLUDE Configuration */ corrected "ram" to "rom" wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections into PRG-RAM bank 0. |- |<code>prg-rom-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-rom-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-rom-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-rom-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections into PRG-RAM. |- |<code>prg-rom-banked-8.ld</code> |One 8KiB bank is visible at 0x8000, and the last 3 banks are fixed contiguously at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two independent 8KiB banks at 0x8000 and 0xa000, and the last 2 banks fixed contiguously at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two independent 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} 7a5be39720f43749dc7bdf4ee82c1c9643b33123 448 446 2023-08-30T04:58:46Z Cogwheel 13 Clarified and added some examples for configuring the project wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |} == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-rom-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-rom-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-rom-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} b702ca369d694a6869cb85073927592904f72f86 449 448 2023-08-31T20:59:15Z Cogwheel 13 Added a bunch more information on mappers with some example code wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Currently Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |} For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell"> $ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes </syntaxhighlight> == Mapper Basics == The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos. === TLDR: What is a mapper? === Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup. To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written". === PPU Banking === The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++"> bank_bg(0); // Use lower half of CHR-ROM for backgrounds bank_spr(1); // Use upper half of CHR-ROM for sprites </syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification. === CHR-ROM Banking === CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++"> // Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM set_chr_bank_0(2); set_chr_bank_1(3); </syntaxhighlight> === PRG-RAM === The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM). Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++"> set_wram_mode(WRAM_ON); </syntaxhighlight> ===== PRG-RAM Banking ===== This gets a bit more complicated [ TODO ] === PRG-ROM Banking === This gets quite a bit more complicated [ TODO ] == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-rom-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-rom-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-rom-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} a49a2c86f08718721b45dfbb3bbecf6588215b2c Character set 0 62 447 2023-08-29T01:13:07Z Mysterymath 3 Add a page on character sets. wikitext text/x-wiki Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code> )to target character sets. With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced. {| class="wikitable" |+Supported Character Sets !Target !Name !Syntax !Notes |- |<code>c64</code> |Unshifted PETSCII |<code>U"HELLO"_u</code> |C0 Control codes are passed through uninterpreted. |- |" |Shifted PETSCII |<code>U"hello"_s</code> |" |- |" |Unshifted Video |<code>U"HELLO"_uv</code> |C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |" |Shifted Video |<code>U"hello"_sv</code> |" |- |" |Unshifted Reverse Video |<code>U"HELLO"_urv</code> |C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these. |- |" |Shifted Reverse Video |<code>U"HELLO"_srv</code> |" |} 2b93767a870e7f67bc360dd48662b5dcb185525b 452 447 2023-09-02T00:09:59Z Mysterymath 3 Add PET character set wikitext text/x-wiki Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code> )to target character sets. With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced. {| class="wikitable" |+Supported Character Sets !Target !Name !Syntax !Notes |- |<code>c64</code> , <code>pet</code> |Unshifted PETSCII |<code>U"HELLO"_u</code> |C0 Control codes are passed through uninterpreted. |- |" |Shifted PETSCII |<code>U"hello"_s</code> |" |- |" |Unshifted Video |<code>U"HELLO"_uv</code> |C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |" |Shifted Video |<code>U"hello"_sv</code> |" |- |" |Unshifted Reverse Video |<code>U"HELLO"_urv</code> |C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these. |- |" |Shifted Reverse Video |<code>U"HELLO"_srv</code> |" |} afddfadfab037ccbee8cbaa72037a1df914af9d9 453 452 2023-09-02T00:11:08Z Mysterymath 3 Use descriptive target names wikitext text/x-wiki Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code> )to target character sets. With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced. {| class="wikitable" |+Supported Character Sets !Target !Name !Syntax !Notes |- |Commodore PET, Commodore 64 |Unshifted PETSCII |<code>U"HELLO"_u</code> |C0 Control codes are passed through uninterpreted. |- |" |Shifted PETSCII |<code>U"hello"_s</code> |" |- |" |Unshifted Video |<code>U"HELLO"_uv</code> |C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |" |Shifted Video |<code>U"hello"_sv</code> |" |- |" |Unshifted Reverse Video |<code>U"HELLO"_urv</code> |C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these. |- |" |Shifted Reverse Video |<code>U"HELLO"_srv</code> |" |} beca2467bdcd84c7b12f0de9188fa056572d5fcb Assembler 0 13 451 297 2023-09-01T22:26:42Z Mysterymath 3 Add a link to the GNU assembler manual wikitext text/x-wiki The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program: asm volatile ("JSR\t$FFD2" : "+a"(c)); To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is [https://sourceware.org/binutils/docs/as/ GNU assembler] compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time. The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. === Target-specific directives === {| class="wikitable" |+ !Syntax !Description |- | .mos_addr_asciz <expression>, <num-digit-chars> |Emits a fixed-length decimal ASCII string of a relocatable expression. Primary use case is for generating the BASIC header on C64. |} [[Category:Assembly]] e713c649c0ef9337477e22830d8ba070e6fd69c9 C calling convention 0 17 456 455 2023-09-02T10:10:33Z Wombat 9 Add C syntax wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2</code>, <code>rc3</code> |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code> |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |- |<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">void f(uint64_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||By value (incl. <code>rc6-7</code>) |} 26c49c5b110792622d9615aea8a5db52fddccb54 457 456 2023-09-02T11:52:23Z Wombat 9 Add return struct examples wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2</code>, <code>rc3</code> |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code> |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |- |<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2, rc3</code> (value) |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | | | |<code>a</code> |<code>a</code> |<code>rc2, rc3</code> (pointer; ''implicit'' 1st arg.) |- |<source lang="c" enclose="none">void f(uint64_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||Always by value (incl. <code>rc6-7</code>) |} f32929f194168f3b66dd6693ddf32b0ac38d2607 458 457 2023-09-02T12:04:17Z Wombat 9 Update calling convention example table wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> | |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> | |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> | |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> | |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> | |- |<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| | |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By value if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return by value |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> |<code>rc2-3</code> |Return via ''implicit'' 1st arg. |- |<source lang="c" enclose="none">void f(uint64_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| |By value, incl. <code>rc6-7</code> |} 19d32293ef42ac3ebdd3d9197bb05c1dc6fc71e8 459 458 2023-09-02T12:33:40Z Wombat 9 Update comments in table wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args always... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (also <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Fill from left to right |- |<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| | |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By value if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return by value |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> |<code>rc2-3</code> |Return via ''implicit'' 1st arg. |} a413fcb027422afb418f80ba711983f3ec0f7069 460 459 2023-09-02T12:38:00Z Wombat 9 Update table comments wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>|| | |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By value if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return by value |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> |<code>rc2-3</code> |Return via ''implicit'' 1st arg. |} 0f5fa8a5a32b88ea5c947086cf7e4253ec194169 461 460 2023-09-02T18:46:50Z Wombat 9 Change "by value" to "by members" wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> |<code>rc2-3</code> |Return via ''implicit'' 1st arg. |} dcb2d6226f388ce54e03257f46590cb8cb7faa55 462 461 2023-09-02T19:28:23Z Mysterymath 3 Clarify wording wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write to ''implicit'' 1st arg. |} f642ab45b25f5e14e68b2e6b5f31d056f88ce62a 463 462 2023-09-02T19:28:49Z Mysterymath 3 wikitext text/x-wiki * A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it. * PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior. * Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument. * The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. * Pointers are passed through RS1 through RS7. * If no registers remain available, values are passed through the soft stack. * Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value. * Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void. * Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64. For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate. Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members. [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)] ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write through ''implicit'' 1st arg pointer |} 90bb88971c006ba641e971e4dad980b60f438438 File talk:LLVM-MOS logo.png 7 63 464 2023-09-04T10:49:30Z 37.113.180.113 0 Created page with "I love this madly, how can I use it? ~~~~" wikitext text/x-wiki I love this madly, how can I use it? [[Special:Contributions/37.113.180.113|37.113.180.113]] 10:49, 4 September 2023 (UTC) e4e8b0ba5c72517b2edcf37e718196d302819fab NES targets 0 54 465 449 2023-09-05T01:39:53Z Jroweboy 14 Added Action 53 mapper wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Currently Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |- |028 |Action 53 |<code>nes-action53</code> |} For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell"> $ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes </syntaxhighlight> == Mapper Basics == The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos. === TLDR: What is a mapper? === Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup. To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written". === PPU Banking === The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++"> bank_bg(0); // Use lower half of CHR-ROM for backgrounds bank_spr(1); // Use upper half of CHR-ROM for sprites </syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification. === CHR-ROM Banking === CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++"> // Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM set_chr_bank_0(2); set_chr_bank_1(3); </syntaxhighlight> === PRG-RAM === The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM). Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++"> set_wram_mode(WRAM_ON); </syntaxhighlight> ===== PRG-RAM Banking ===== This gets a bit more complicated [ TODO ] === PRG-ROM Banking === This gets quite a bit more complicated [ TODO ] == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} == MMC1 == Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-16.ld</code> |16 KiB of PRG-ROM without banking. |- |<code>prg-rom-32.ld</code> |32 KiB of PRG-ROM without banking. |- |<code>prg-rom-32-banked.ld</code> |32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0. |- |<code>prg-rom-64.ld</code> |64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-128.ld</code> |128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. |- |<code>prg-rom-256.ld</code> |256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == Action 53 == This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000. eb93313d6f55976542aa12919decf1bb55b596b3 469 465 2023-09-14T18:17:29Z Mysterymath 3 Update MMC1 to reflect simplication. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Currently Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |- |028 |Action 53 |<code>nes-action53</code> |} For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell"> $ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes </syntaxhighlight> == Mapper Basics == The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos. === TLDR: What is a mapper? === Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup. To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written". === PPU Banking === The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++"> bank_bg(0); // Use lower half of CHR-ROM for backgrounds bank_spr(1); // Use upper half of CHR-ROM for sprites </syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification. === CHR-ROM Banking === CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++"> // Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM set_chr_bank_0(2); set_chr_bank_1(3); </syntaxhighlight> === PRG-RAM === The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM). Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++"> set_wram_mode(WRAM_ON); </syntaxhighlight> ===== PRG-RAM Banking ===== This gets a bit more complicated [ TODO ] === PRG-ROM Banking === This gets quite a bit more complicated [ TODO ] == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} == MMC1 == The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked.ld</code> |Up to 256KiB of banked PRG-ROM. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == Action 53 == This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000. a9c8e79a307482468a380fad897d126315cfb3ce 471 469 2023-09-16T06:07:39Z Mysterymath 3 Refer to ines.h macros wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Currently Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |- |028 |Action 53 |<code>nes-action53</code> |} For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell"> $ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes </syntaxhighlight> == Mapper Basics == The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos. === TLDR: What is a mapper? === Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup. To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written". === PPU Banking === The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++"> bank_bg(0); // Use lower half of CHR-ROM for backgrounds bank_spr(1); // Use upper half of CHR-ROM for sprites </syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification. === CHR-ROM Banking === CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++"> // Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM set_chr_bank_0(2); set_chr_bank_1(3); </syntaxhighlight> === PRG-RAM === The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM). Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++"> set_wram_mode(WRAM_ON); </syntaxhighlight> ===== PRG-RAM Banking ===== This gets a bit more complicated [ TODO ] === PRG-ROM Banking === This gets quite a bit more complicated [ TODO ] == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with the macros in <code>[https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/nes/ines.h ines.h]</code>, inline assembly, or an assembly . For example:<syntaxhighlight lang="c"> // main.c #include <ines.h> MAPPER_CHR_ROM_KB(0); MAPPER_CHR_RAM_KB(8); </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight><syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} == MMC1 == The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked.ld</code> |Up to 256KiB of banked PRG-ROM. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == Action 53 == This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000. 8929ddd1a9d2b6e2022c688d2a2c30d86231ce6c 472 471 2023-09-16T06:08:03Z Mysterymath 3 /* Symbol Configuration */ wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Currently Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |- |028 |Action 53 |<code>nes-action53</code> |} For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell"> $ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes </syntaxhighlight> == Mapper Basics == The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos. === TLDR: What is a mapper? === Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup. To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written". === PPU Banking === The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++"> bank_bg(0); // Use lower half of CHR-ROM for backgrounds bank_spr(1); // Use upper half of CHR-ROM for sprites </syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification. === CHR-ROM Banking === CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++"> // Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM set_chr_bank_0(2); set_chr_bank_1(3); </syntaxhighlight> === PRG-RAM === The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM). Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++"> set_wram_mode(WRAM_ON); </syntaxhighlight> ===== PRG-RAM Banking ===== This gets a bit more complicated [ TODO ] === PRG-ROM Banking === This gets quite a bit more complicated [ TODO ] == Sections == === PRG-ROM === If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. === CHR-ROM === If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. === PRG-RAM === If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. == Symbol Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with the macros in <code>[https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/nes/ines.h ines.h]</code>, inline assembly, or an assembly . For example:<syntaxhighlight lang="c"> // main.c #include <ines.h> MAPPER_CHR_ROM_KB(0); MAPPER_CHR_RAM_KB(8); </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight><syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low 2 bytes of byte 14 |- |<code>__default_expansion_device</code> |Low 6 bytes of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == INCLUDE Configuration == In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} == [https://www.nesdev.org/wiki/NROM NROM], CNROM == === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} == MMC1 == The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked.ld</code> |Up to 256KiB of banked PRG-ROM. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == MMC3 == By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. === INCLUDE Configuration === {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} == Action 53 == This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000. 92eee72902981237cbc83be88502b7933c8f3cee 474 472 2023-09-16T12:40:03Z Asie 12 Add new mappers, clean up category tree, add ELF address space documentation, fix a few typos. wikitext text/x-wiki The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges. The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target: {| class="wikitable" |+Currently Supported Mappers !Number !Name !Target |- |000 |NROM |<code>nes-nrom</code> |- |001 |MMC1 |<code>nes-mmc1</code> |- |002 |UNROM |<code>nes-unrom</code> |- |003 |CNROM |<code>nes-cnrom</code> |- |004 |MMC3 |<code>nes-mmc3</code> |- |028 |Action 53 |<code>nes-action53</code> |- |030 |UNROM-512 |<code>nes-unrom-512</code> |} For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell"> $ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes </syntaxhighlight> == Mapper Basics == The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos. === TLDR: What is a mapper? === Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup. To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written". === PPU Banking === The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++"> bank_bg(0); // Use lower half of CHR-ROM for backgrounds bank_spr(1); // Use upper half of CHR-ROM for sprites </syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification. === CHR-ROM Banking === CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++"> // Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM set_chr_bank_0(2); set_chr_bank_1(3); </syntaxhighlight> === PRG-RAM === The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM). Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++"> set_wram_mode(WRAM_ON); </syntaxhighlight> ===== PRG-RAM Banking ===== This gets a bit more complicated [ TODO ] === PRG-ROM Banking === This gets quite a bit more complicated [ TODO ] == Mapper/iNES Header Configuration == As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10. You can set these symbols with the macros in <code>[https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/nes/ines.h ines.h]</code> or manually - using inline assembly or a separate assembly file. For example:<syntaxhighlight lang="c"> // main.c #include <ines.h> MAPPER_CHR_ROM_KB(0); MAPPER_CHR_RAM_KB(8); </syntaxhighlight><syntaxhighlight lang="c"> // main.c asm(".globl __chr_rom_size\n" "__chr_rom_size = 0\n" ".globl __chr_ram_size\n" "__chr_ram_size = 8\n"); </syntaxhighlight><syntaxhighlight lang="asm"> ; config.s .global __prg_rom_size, __chr_rom_size, __prg_ram_size .global __mirroring, __four_screen ; Kilobytes __prg_rom_size = 512 __chr_rom_size = 256 __prg_ram_size = 8 ; Flags __mirroring = 1 ; horizontal mirroring </syntaxhighlight> {| class="wikitable" |+Configuration Symbols !Symbol !Description |- |<code>__prg_rom_size</code> |KiB of PRG-ROM |- |<code>__chr_rom_size</code> |KiB of CHR-ROM |- |<code>__prg_ram_size</code> |KiB of PRG-RAM |- |<code>__prg_nvram_size</code> |KiB of PRG-NVRAM |- |<code>__chr_ram_size</code> |KiB of CHR-RAM |- |<code>__chr_nvram_size</code> |KiB of CHR-NVRAM |- |<code>__mirroring</code> |Bit 0 of field 6 |- |<code>__battery</code> |Bit 1 of field 6 |- |<code>__trainer</code> |Bit 2 of field 6 |- |<code>__four_screen</code> |Bit 3 of field 6 |- |<code>__mapper</code> |Mapper number. |- |<code>__console_type</code> |Low two bits of field 7 |- |<code>__timing</code> |Low two bits of field 12 |- |<code>__ppu_type</code> |Low nibble of byte 13 when Vs. System |- |<code>__hw_type</code> |High nibble of byte 13 when Vs. System |- |<code>__extended_console_type</code> |Low nibble of byte 13 when Extended Console Type |- |<code>__misc_roms</code> |Low two bits of byte 14 |- |<code>__default_expansion_device</code> |Low six bits of byte 15 |- |<code>__prg_rom_size_raw</code> |The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>. |- |<code>__chr_rom_size_raw</code> |The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>. |- |<code>__prg_ram_size_raw</code> |Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>. |- |<code>__prg_nvram_size_raw</code> |Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>. |- |<code>__chr_ram_size_raw</code> |Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>. |- |<code>__chr_nvram_size_raw</code> |Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>. |} == Linker layout == === Sections === ==== PRG-ROM ==== If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM. ==== CHR-ROM ==== If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM. ==== PRG-RAM ==== If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored. === INCLUDE Configuration === In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets. You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile"> # makefile LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0 </syntaxhighlight> {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>common.ld</code> |Functionality common to all linker scripts for a given target. Must be included. Included in default linker script. |- |<code>c-in-ram.ld</code> |Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script. |} === ELF address space === On the NES target, the ELF address space is split into three sections: {| class="wikitable" |+NES target - LMA format !Type (bits 31-24) !Bits !Name !Values |- | rowspan="2" | <code>0x00</code> (CPU) |23 - 16 |Bank |<code>0x00</code> .. <code>0xFF</code> |- |15 - 0 |Address |<code>0x0000</code> .. <code>0xFFFF</code> Matches 16-bit address for the CPU |- | <code>0x01</code> (CHR) |23 - 0 |Address |<code>0x000000</code> .. <code>0xFFFFFF</code> |} == Mapper-specific information == === [https://www.nesdev.org/wiki/NROM NROM], CNROM === ==== INCLUDE Configuration ==== {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |} === MMC1 === The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space. ==== INCLUDE Configuration ==== {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram-0.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked.ld</code> |Up to 256KiB of banked PRG-ROM. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} === MMC3 === By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts. In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number. ==== INCLUDE Configuration ==== {| class="wikitable" |+INCLUDE Files !Name !Description |- |<code>c-in-prg-ram.ld</code> |Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM. |- |<code>prg-rom-banked-8.ld</code> |One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000. |- |<code>prg-rom-banked-mode-0.ld</code> |Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000. |- |<code>prg-rom-banked-mode-1.ld</code> |Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000. |- |<code>prg-rom-fixed.ld</code> |Up to 32KiB of fixed PRG-ROM. |} === Action 53 === This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000. 397b33814ee8f0efbd02c9cd54a06a292d96cee8 Clangd 0 55 466 366 2023-09-06T07:45:22Z Mysterymath 3 Call out that the asterisks are literal wikitext text/x-wiki The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK. The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though: # Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory. # Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd. # Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. Note that the asterisks are literal; they function as wildcards. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers. == CMake + Visual Studio Code == To set up clangd when building using CMake and editing with Visual Studio Code: # Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd. # Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators. # Build the project. # In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd." # In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. Note that the asterisks are literal; they function as wildcards. # Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path. # Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install. 11c43171aa9adb60b5c17c73ae33bffad88fd2f4 468 466 2023-09-14T17:34:05Z Cogwheel 13 Added a case-sensitivity warning for Windows users wikitext text/x-wiki The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK. The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though: # Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory. # Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd. # Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. Note that the asterisks are literal; they function as wildcards. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers. '''Note for Windows users''': the <code>query-driver</code> path is case-sensitive. Use a capital drive letter. == CMake + Visual Studio Code == To set up clangd when building using CMake and editing with Visual Studio Code: # Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd. # Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators. # Build the project. # In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd." # In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. Note that the asterisks are literal; they function as wildcards. # Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path. # Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install. 17f6cb4337d5d75b2a845036e060e5c6df723d81 470 468 2023-09-14T19:33:17Z Cogwheel 13 further clarification for windows wikitext text/x-wiki The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK. The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though: # Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory. # Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd. # Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. Note that the asterisks are literal; they function as wildcards. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers. '''Note for Windows users''': the <code>query-driver</code> path is case-sensitive. Use a capital drive letter and backslash <code>\</code> for the path separator. == CMake + Visual Studio Code == To set up clangd when building using CMake and editing with Visual Studio Code: # Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd. # Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators. # Build the project. # In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd." # In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. Note that the asterisks are literal; they function as wildcards. # Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path. # Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install. 814f7f265b1d99ccfccbc2509642456b0b74931f C compiler 0 18 467 265 2023-09-09T18:38:04Z Asie 12 document defines wikitext text/x-wiki A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Different CPUs may be selected using the <code>-mcpu</code> flag, for example <code>-mcpu=mosw65816</code>. == Defines == The compiler defines the <code>__mos__</code> macro for the <code>mos</code> target. In addition, defines are added for every CPU which is compatible with the code; for instance <code>-mcpu=mos65c02</code> additionally defines <code>__mos6502__</code> and <code>__mos65c02__</code>. == Known issues == The Clang is broadly compatible with the freestanding portion of the C99 standard, with one big caveat: * No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. Some of the GCC and Clang language extensions work, some don't. Similarly some of the C++ language features work, some don't. We haven't exhaustively catalogued this; there's a lot, and some are extremely obscure and nearly useless. Still, some compiler extensions are de-facto standard, so we'll call out a few caveats for those: * The GCC/Clang alignment directives work only for global and static variables, not automatic (local) variables. Aligning the stack pointer is tricky, since it requires use of a frame pointer; but the rigors of 6502 indexing cause our frame pointer to be useless for this purpose. To get this to work, we may need *two* frame pointers. Expected call for this feature is low, so if you need it, let us know, and we'll reprioritize. [[Category:C]] 7f08d28c0b00634a674bcb20f6b101434276af3c Rationale 0 26 473 303 2023-09-16T06:11:55Z Mysterymath 3 Remove out of date line about Rust and C++ wikitext text/x-wiki == Why yet another 6502 C compiler? == After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it? == Performance == As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program. At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers. Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow). As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code. == Features == Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically. llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level. Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing. See also [[Findings]]. [[Category:Main]] 7e4a8ae83efe64d65f54dda2c67ab01da4241940 Character set 0 62 475 453 2023-09-16T12:48:03Z Asie 12 Prettify table, add cx16/atari8 charsets. wikitext text/x-wiki Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code>) to target character sets. With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced. {| class="wikitable" |+Supported Character Sets !Target !Name !Syntax !Notes |- | rowspan="6" | Commodore PET, Commodore 64, Commander X16 |Unshifted PETSCII |<code>U"HELLO"_u</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. |- |Shifted PETSCII |<code>U"hello"_s</code> |- |Unshifted Video |<code>U"HELLO"_uv</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |Shifted Video |<code>U"hello"_sv</code> |- |Unshifted Reverse Video |<code>U"HELLO"_urv</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these. |- |Shifted Reverse Video |<code>U"HELLO"_srv</code> |- |Commander X16 |ISO-8859-15 |<code>U"Hello"_i</code> |C0 and C1 Control codes are passed through uninterpreted. Corresponds to the "ISO mode". |- | rowspan="6" | Atari 8-bit |ATASCII |<code>U"Hello"_a</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. |- |International ATASCII |<code>U"hello"_i</code> |- |ATASCII Video |<code>U"HELLO"_av</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |ATASCII International Video |<code>U"hello"_iv</code> |- |ATASCII Reverse Video |<code>U"HELLO"_arv</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. |- |ATASCII International Reverse Video |<code>U"HELLO"_irv</code> |} 3330bba4e5144581692badccce14665d73702e3d 476 475 2023-09-16T12:48:21Z Asie 12 wikitext text/x-wiki Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code>) to target character sets. With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced. {| class="wikitable" |+Supported Character Sets !Target !Name !Syntax !Notes |- | rowspan="6" | Commodore PET, Commodore 64, Commander X16 |Unshifted PETSCII |<code>U"HELLO"_u</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. |- |Shifted PETSCII |<code>U"hello"_s</code> |- |Unshifted Video |<code>U"HELLO"_uv</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |Shifted Video |<code>U"hello"_sv</code> |- |Unshifted Reverse Video |<code>U"HELLO"_urv</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these. |- |Shifted Reverse Video |<code>U"HELLO"_srv</code> |- |Commander X16 |ISO-8859-15 |<code>U"Hello"_i</code> |C0 and C1 Control codes are passed through uninterpreted. Corresponds to the "ISO mode". |- | rowspan="6" | Atari 8-bit |ATASCII |<code>U"Hello"_a</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. |- |International ATASCII |<code>U"Hello"_i</code> |- |ATASCII Video |<code>U"Hello"_av</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. |- |ATASCII International Video |<code>U"Hello"_iv</code> |- |ATASCII Reverse Video |<code>U"Hello"_arv</code> | rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. |- |ATASCII International Reverse Video |<code>U"Hello"_irv</code> |} 11ea5914b639f730f5502b9a84b9a662640d5c92 Imaginary registers 0 42 477 294 2023-09-17T19:16:07Z Mysterymath 3 Add an explicit description of imaginary register layout. wikitext text/x-wiki The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify. Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. Because "virtual registers" has another predefined meaning in LLVM land, we use the term '''imaginary register''' to refer to a byte in zero page memory. It's represented at codegen time by a symbol like __rc17 or __rs5, which is then translated to an actual memory address by the linker at link time. Presently, the compiler requires 16 imaginary pointers (two contiguous bytes each). These are called rs0-rs15. Each is subdivided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0-rc31. LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 88212f11a75eff6a417831240f0e75228def62ed C Inline Assembly 0 40 478 378 2023-09-23T03:30:56Z Jroweboy 14 wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. [[Category:C]] [[Category:Assembly]] dfae2cc2ad51cff1a9244225e917727e7bf8de3c Zero page 0 60 479 442 2023-09-25T02:55:37Z Mysterymath 3 Add a blurb about zero page pointers. wikitext text/x-wiki The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may reserving zero page memory from the compiler. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]]. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using e.g. <code>char __zp foo;</code>or <code>char __zeropage foo;</code> . The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler, and pointers to such variables can also be declared to be in the zero page, which allows them to fit in 8 bits. For example, <code>char __zp *fptr = &foo; assert(sizeof(fptr) == 1);</code> . While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program. [[Category:Code generation]] [[Category:C]] 33020f53aba5e689767bd07d9da367ef44454c26 ELF specification 0 15 480 434 2023-09-26T14:58:27Z Asie 12 0.3.0: SPC700 support, relocation type fix wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.0 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [[Category:Linking]] aee6b4467a66811d0a7022d1a32f5f9c659a3e30 481 480 2023-09-26T15:02:19Z Asie 12 add SPC-700 docs to references wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.0 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] 70dc467999374c178d605cfaa23e7378d3f61e27 482 481 2023-10-09T21:14:39Z Jbyrd 1 /* Machine type field */ Should to shall wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.0 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] 8f5435fe78375e3d143af99c87aa41efb68bfea3 Modifiers 0 16 483 380 2023-10-12T06:45:58Z Jroweboy 14 Try to fix the syntax being parsed as email wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar). [[Category:Assembly]] a206651555e4666ba44d36329fcb347e7cd62ae9 Welcome 0 1 484 450 2023-10-29T01:58:17Z Jbyrd 1 Trivial edit to test editing wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing, public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 736e3cf6d3b6c816c85fcb5fc6f3bcc454feeb72 494 484 2023-12-24T21:15:07Z Jbyrd 1 Trivial edit to test editing wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets. This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]]. Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code. Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 73d010ff8df6da5c1c254ca2c0dbe09620b14a7d 496 494 2024-01-10T23:48:41Z Jbyrd 1 Rewrite introduction with more of an overview wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference]. Our work encompasses the following: * A fork of the LLVM project that provides first-class support to the MOS 6502 and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including IEEE-754 floating point.  Our compiler implements several novel approaches in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented complete ELF support for the 6502, which permits us to use the existing suite of LLVM tools for creating, analyzing and modifying 6502-specific object files and libraries. * An SDK project that comprises target-specific code for common 65xx-based microcomputers.  It includes a minimal C standard library with support for memory management and text output.  Some example programs are included.  We currently support over two dozen targets, including a host-based simulator.  The list of supported targets is growing rapidly. * An automated test and packaging infrastructure.  We use Github runners to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests.  We also run automated benchmarks using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 31f18848839a605db796f19546321a1be57ec4d9 497 496 2024-01-11T00:34:34Z Jbyrd 1 Added lots of links wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including IEEE-754 floating point.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo several novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing suite of LLVM tools for creating, analyzing and modifying 6502-specific object files and libraries. * A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a minimal C standard library with support for memory management and text output.  Some example programs are included.  We currently support over two dozen targets, including a host-based simulator.  The list of supported targets is growing rapidly. * An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests.  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} eef1f6a33b9f8a55279f20df44ae5629813245bd 498 497 2024-01-11T00:38:29Z Jbyrd 1 More links wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point].  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing suite of LLVM tools for creating, analyzing and modifying 6502-specific object files and libraries. * A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some example programs are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a host-based simulator.  The list of supported targets is growing rapidly. * An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests.  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 794fc718c003cdc0569beda24338f57c6a4a9f7b 499 498 2024-01-11T00:41:27Z Jbyrd 1 Yet more links wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point].  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries. * A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests.  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 195014c6d378e3fa4080257815d15d9d8fdc81f6 500 499 2024-01-11T00:42:21Z Jbyrd 1 Little more commentary wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries. * A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests.  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 0ea2a1bae5e53c009f8e72506d424756f6bdf0bc 501 500 2024-01-11T00:43:05Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries. * A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} cc2294210f13fd0e75388435c5cce23245d4d293 502 501 2024-01-11T00:44:19Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} e4cb2970effb306016319e239ea7066f71ab0e12 503 502 2024-01-11T00:45:14Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} cb162de32a385aad663e24f44ecb0742478f5f60 504 503 2024-01-11T00:45:32Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} e645938ccead34e375b0e5723635b63758e67997 505 504 2024-01-11T00:57:29Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point]; first-class [[Assembler|integrated 65xx assembler support]]; and clang's world-class error messages.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 2d2b246a5662ee1a050cb426222131992e724361 506 505 2024-01-11T00:58:31Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 9ceacb4fa7e4845ec72a246d441c9e2d47d40f96 Cc65 integration 0 64 485 2023-10-29T02:19:19Z Mysterymath 3 Add a page on cc65 integration wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments. This feature cannot be used during a relocatable link (<code>-r</code>). {| class="wikitable" |+Caption text |- !Header text!!Header text |- |Example||Example |- |Example||Example |- |Example||Example |- |Example||Example |- |Example||Example |- |Example||Example |- |Example||Example |} 27d9432ee20b0078f4cf118441094efa169768e9 486 485 2023-10-29T02:22:10Z Mysterymath 3 Fill out escape sequence table wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments. This feature cannot be used during a relocatable link (<code>-r</code>). {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} 2c7048559937915a9a3a66646b79a36b3558d12e 487 486 2023-10-29T02:23:45Z Mysterymath 3 Add hex encoding wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments. This feature cannot be used during a relocatable link (<code>-r</code>). {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_XX</code> |The byte given by hex string <code>XX</code> (case insensitive) |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} 96e2c0333a0bf4c2c9b84ed9a871d4f61e6dc949 488 487 2023-10-29T02:25:52Z Mysterymath 3 Move to C and linking categories wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments. This feature cannot be used during a relocatable link (<code>-r</code>). {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_XX</code> |The byte given by hex string <code>XX</code> (case insensitive) |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} [[Category:C]] [[Category:Linking]] 778a5e1e32960e9d9c26d773a25f622d9b7d210b 490 488 2023-10-30T05:18:03Z Mysterymath 3 Mention that cc65 default segments are automatically placed wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section. This feature cannot be used during a relocatable link (<code>-r</code>). {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_XX</code> |The byte given by hex string <code>XX</code> (case insensitive) |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} [[Category:C]] [[Category:Linking]] 6b170b552e9fd586c4f3bddfcde6b364a6bc6b9a 491 490 2023-10-30T05:56:22Z 172.69.135.138 0 Copy edit wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section. This feature cannot be used during a relocatable link (<code>-r</code>), since the output is a relocatable ELF file, and ca65's relocations are much richer than LLVM-MOS ELF allows. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names. The default cc65 segments are automatically assigned the correct type and flags. {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_XX</code> |The byte given by hex string <code>XX</code> (case insensitive) |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} [[Category:C]] [[Category:Linking]] 2d6749e2e83892a5ee32d91ac2a9b3a0821c7c11 492 491 2023-11-02T16:39:46Z Mysterymath 3 Update hex escape syntax wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section. This feature cannot be used during a relocatable link (<code>-r</code>), since the output is a relocatable ELF file, and ca65's relocations are much richer than LLVM-MOS ELF allows. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names. The default cc65 segments are automatically assigned the correct type and flags. {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_xXX</code> |The byte given by hex string <code>XX</code> (case insensitive) |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} [[Category:C]] [[Category:Linking]] 5bf6b14b6f6500f71a6719f7ca293f478f1894b2 C interrupts 0 41 489 349 2023-10-29T03:56:16Z Mysterymath 3 Update incorrect register save/restore sequence wikitext text/x-wiki == Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts. Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this. Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved. Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite. == Challenges == The nature of the 6502 presents some challenges to using this model. === Static Stack Allocation === The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time. However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property. == Interrupt Annotations == To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr". === "interrupt" Attribute === This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack. === "interrupt_norecurse" Attribute === This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves. === "no_isr" Attribute === This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness. ==== Manual Interrupt Sequence ==== Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler. Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is written entirely in assembly. The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm"> cld pha txa pha tya pha lda mos8(__rc2) pha lda mos8(__rc3) pha lda mos8(__rc4) pha lda mos8(__rc5) pha lda mos8(__rc6) pha lda mos8(__rc7) pha lda mos8(__rc8) pha lda mos8(__rc9) pha lda mos8(__rc10) pha lda mos8(__rc11) pha lda mos8(__rc12) pha lda mos8(__rc13) pha lda mos8(__rc14) pha lda mos8(__rc15) pha lda mos8(__rc16) pha lda mos8(__rc17) pha lda mos8(__rc18) pha lda mos8(__rc19) pha JSR body pla sta mos8(__rc19) pla sta mos8(__rc18) pla sta mos8(__rc17) pla sta mos8(__rc16) pla sta mos8(__rc15) pla sta mos8(__rc14) pla sta mos8(__rc13) pla sta mos8(__rc12) pla sta mos8(__rc11) pla sta mos8(__rc10) pla sta mos8(__rc9) pla sta mos8(__rc8) pla sta mos8(__rc7) pla sta mos8(__rc6) pla sta mos8(__rc5) pla sta mos8(__rc4) pla sta mos8(__rc3) pla sta mos8(__rc2) pla tay pla tax pla rti </syntaxhighlight> === Undefined Behavior === It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active. [[Category:C]] 7622af06b1707fd7a96d7cceb0e4ca9d6fbb94b0 Building llvm-mos on MacOS 0 28 495 181 2024-01-04T09:01:08Z Wombat 9 Add link to llvm building video wikitext text/x-wiki The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed: <cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=${WORKSPACE}/build/stage2/install -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all cmake --build . --config Release --target check-all cmake --build . --config Release --target install</code> There is also a [https://www.youtube.com/embed/CGX96RmmlLQ?si=12HOY3id7mOhXXBS video walk-through] for MacOS and Linux. [[Category:Building]] 6b78e4be184ee2483b249a4f2929af2d760b9b06 Welcome 0 1 507 506 2024-01-11T01:00:30Z Jbyrd 1 /* Welcome to the llvm-mos project! */ wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  This includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 54df9ea6b91f6707b15b0892d40b87a2862b6fbc 508 507 2024-01-11T01:01:51Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 6fecc9bada27e216b237d64924916d0eb39ffdc1 513 508 2024-01-29T18:31:18Z Jbyrd 1 Reorder list wikitext text/x-wiki [[Category:Main]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 19fec060818facb9c4dd7329787ab8e5c3b94c92 Ca65 comparison 0 65 509 2024-01-24T05:01:52Z Mysterymath 3 Created page with "This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]]. ; ca65 .segment "PRG_ROM_1" ; llvm-mos .section .prg_rom_1 ; ca65 .global _score _score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code ; llvm-mos .global score score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here ; ca65 bn..." wikitext text/x-wiki This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]]. ; ca65 .segment "PRG_ROM_1" ; llvm-mos .section .prg_rom_1 ; ca65 .global _score _score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code ; llvm-mos .global score score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here ; ca65 bne :+ ; add '+'s to jump to the next ':'s, or use '-'s to jump to previous ':'s inc counter : ; llvm-mos bne 1f ; 'f' means the next label '1' forward from here, 'b' would look backwards instead, can use other numbers for different local labels inc counter 1: d97164126c56096581c7c7cc5c3358cd09d5dcab 510 509 2024-01-24T05:02:36Z Mysterymath 3 Formatting wikitext text/x-wiki This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]]. ; ca65 .segment "PRG_ROM_1" ; llvm-mos .section .prg_rom_1 ; ca65 .global _score _score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code ; llvm-mos .global score score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here ; ca65 bne :+ ; add '+'s to jump to the next ':'s, or use '-'s to jump to previous ':'s inc counter : ; llvm-mos bne 1f ; 'f' means the next label '1' forward from here, 'b' would look backwards instead ; can use other numbers for different local labels inc counter 1: bd3c5b69a84d06e08d8b9f4b56e15cf3c79e22fd 511 510 2024-01-24T05:03:16Z Mysterymath 3 wikitext text/x-wiki This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]]. ; ca65 .segment "PRG_ROM_1" ; llvm-mos .section .prg_rom_1 ; ca65 .global _score _score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code ; llvm-mos .global score score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here ; ca65 bne :+ ; add '+'s to jump to the next ':'s, or use '-'s to jump to previous ':'s inc counter : ; llvm-mos bne 1f ; 'f' means the next label '1' forward from here, 'b' would look backwards instead ; can use other numbers for different local labels inc counter 1: [[Category:Assembly]] e289df0d246eaba80a0dbac13fe83f11ddbcb097 Porting 0 47 512 331 2024-01-26T21:21:34Z 172.71.151.117 0 minimal ld script needed updating wikitext text/x-wiki We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so. == Imaginary Target == For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one. We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian. == The Simplest Program == First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!) Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c"> int main(void) { return 0; } </syntaxhighlight> == Parent Target == The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries. For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch. Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets. Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>. == Compiling: First Attempt == Let's compile:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c ld.lld: error: cannot find linker script link.ld </syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>. == Linker Script == The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]). There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started. Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { zp : ORIGIN = __rc31 + 1, LENGTH = 0x100 - (__rc31 + 1) ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 } REGION_ALIAS("c_readonly", ram) REGION_ALIAS("c_writeable", ram) SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") </syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM. The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want. The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer. == Compiling: Second Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main ... $ file main main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped </syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session"> $ llvm-nm main 00000209 B __bss_end 00000000 A __bss_size 00000209 B __bss_start 00000209 T __data_end 00000209 A __data_load_start 00000000 A __data_size 00000209 T __data_start 00000209 T __fini_array_end 00000209 T __fini_array_start 00000209 B __heap_start 00000209 T __init_array_end 00000209 T __init_array_start 00000000 A __rc0 00000001 A __rc1 ... 00000009 A __rc9 00000203 T _fini 00000200 T _init 00000200 T _start 00000204 T main </syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately. You can get a disassembly too:<syntaxhighlight lang="shell-session"> $ llvm-objdump -d --print-imm-hex main main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>However, none of this helps make an output file that our emulator can actually load. == Output Format == To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>. Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>. == Compiling: Third Attempt == <syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c $ ls -l ... main main.elf ... $ file main main:data $ file main.elf main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped $ hexdump -C main 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00010000 00 02 |..| 00010002 </syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script. Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>. == Optional Libraries == Take another look at the disassembly:<syntaxhighlight lang="shell-session"> main: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 04 02 jsr $204 00000203 <_fini>: 203: 60 rts 00000204 <main>: 204: a2 00 ldx #$0 206: a9 00 lda #$0 208: 60 rts </syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land. This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors). Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target. The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target]. One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities: {| class="wikitable" !Exit Library !Description |- |<code>exit-custom</code> |Run some custom code (e.g., execute a special BRK, which goes into an underlying OS). |- |<code>exit-loop</code> |Go into an infinite loop. |- |<code>exit-return</code> |Return from <code>_start</code> |} Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>. There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you. We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text"> MEMORY { ram : ORIGIN = 0x0000, LENGTH = 0x10000 user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00 } SECTIONS { INCLUDE c.ld } __rc0 = 0x00; INCLUDE imag-regs.ld ASSERT(__rc0 == 0x00, "Inconsistent zero page map.") ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.") /* Top of stack would be 0x10000, but wrap around. */ __stack = 0; OUTPUT_FORMAT { FULL(ram) SHORT(_start) } </syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session"> $ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack $ llvm-objdump -d --print-imm-hex main.elf main.elf: file format elf32-mos Disassembly of section .text: 00000200 <_start>: 200: 20 07 02 jsr $207 00000203 <__after_main>: 203: 4c 0c 02 jmp $20c 00000206 <_fini>: 206: 60 rts 00000207 <main>: 207: a2 00 ldx #$0 209: a9 00 lda #$0 20b: 60 rts 0000020c <exit>: 20c: 20 06 02 jsr $206 20f: 4c 0f 02 jmp $20f </syntaxhighlight>Nothing changed! Is there a problem? Nope! Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.' = Extending the SDK = After porting LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated. To do so, continue onward to the [[Extending SDK]] guide. a7c0abc7b9f7170d29a595182bbd30f64c53044c C Inline Assembly 0 40 514 478 2024-01-31T21:16:09Z Jbyrd 1 You can use mos-clang as a compiler wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: asm volatile ("t%0a" :: "d"(c)); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 1eba097da80998d01546e6c1f49687e2610306a4 519 514 2024-03-04T11:55:50Z Wombat 9 Slightly more verbose and add missing A clobber wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following: unsigned char c = 1; asm volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 8f2dc5a6eade6eccdf014196527bcb5156ddad9c 520 519 2024-03-04T11:59:23Z Wombat 9 Add link to GAS manual for constrained operands wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following: unsigned char c = 1; asm volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>. Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 0455a2a7fb7411753e50154f5dbc1ca8326d74ae 521 520 2024-03-04T13:14:45Z Wombat 9 Use syntax highlighted code block wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>: <syntaxhighlight lang="c"> unsigned char c = 1; asm volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); </syntaxhighlight> Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 4167d8d35513ed97a842753381b94dfb7025571a 544 521 2024-08-25T16:35:26Z Wombat 9 Add leaf attribute and use `__asm__` over `asm`. wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>: <syntaxhighlight lang="c"> unsigned char c = 1; __attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); </syntaxhighlight> Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 2a298e659c3a1bb1d5a840d813036b81560bee63 545 544 2024-08-27T14:46:12Z Wombat 9 Add info about "p" clobber and 8/16 but operands wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit (<code>__rcxx</code>) or 16-bit (<code>__rsxx</code>) imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>: <syntaxhighlight lang="c"> unsigned char c = 1; __attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); </syntaxhighlight> Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list with e.g. <code>"c"</code>, <code>"v"</code>, or <code>"p"</code>. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 47a6ca25727732efa46098cafdedabc7bbbe3709 549 545 2024-10-22T20:08:16Z Jbyrd 1 Added note on compiler crashes with misuse wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit (<code>__rcxx</code>) or 16-bit (<code>__rsxx</code>) imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. These modifiers are highly case-sensitive, and violating constraint use in inline assembly can [https://github.com/llvm-mos/llvm-mos/issues/463 crash the compiler], so use with a high degree of caution. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>: <syntaxhighlight lang="c"> unsigned char c = 1; __attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); </syntaxhighlight> Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list with e.g. <code>"c"</code>, <code>"v"</code>, or <code>"p"</code>. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] a024213f07c0aa47b49f3ac259dd7a454e54daff 554 549 2024-10-22T20:25:11Z Jbyrd 1 wikitext text/x-wiki Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions: * Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code> * Goto Labels are unsupported. "General purpose registers" for the purposes of inline assembly are taken to be 8-bit (<code>__rcxx</code>) or 16-bit (<code>__rsxx</code>) imaginary registers, depending on the size of the operand. The compiler also supports a few MOS-specific constraints: * <code>a</code>: The <code>A</code> register. * <code>x</code>: The <code>X</code> register. * <code>y</code>: The <code>Y</code> register. * <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>. * <code>d</code>: Either <code>X</code> or <code>Y</code>. * <code>c</code>: The <code>C</code> flag. * <code>v</code>: The <code>V</code> flag. These modifiers are case-sensitive, and violating constraint use in inline assembly can [https://github.com/llvm-mos/llvm-mos/issues/463 crash the compiler], so use with a high degree of caution. The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>: <syntaxhighlight lang="c"> unsigned char c = 1; __attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a"); </syntaxhighlight> Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]] One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list with e.g. <code>"c"</code>, <code>"v"</code>, or <code>"p"</code>. If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like: mos-clang -c my-assembly-file.s [[Category:C]] [[Category:Assembly]] 5257a236f123a31f9c35ec75c11f82ea934ba609 Community 0 66 515 2024-02-09T16:58:49Z Mysterymath 3 Add a community links page wikitext text/x-wiki llvm-mos has a vibrant community both officially and unofficially affiliated with it. The main official hub is our [https://discord.gg/D9gRm3aznh Discord]; drop by if you have questions or would like to contribute. There are also a number of other projects loosely affiliated with llvm-mos; here are links to a few. {| class="wikitable" |+ !Site !Description |- |[https://github.com/MEGA65/mega65-libc mega65-libc] |C library for the Mega65. More thorough platform-specific code than present for the Mega65 llvm-mos SDK. Works with CC65, Clang, and KickC. |- |[https://mysterymath.neocities.org mysterymath's blog] |Current llvm-mos maintainer's blog. llvm-mos internals discussed often at a very thorough level of detail. |- |[https://github.com/mrk-its/rust-mos rust-mos] |Rust frontend port based on llvm-mos. Usually trails somewhat behind llvm-mos's releases. |} 3317bc170ffdbcd882817e27e3dfe3446b94501d 516 515 2024-02-09T16:59:53Z Mysterymath 3 Add to main category wikitext text/x-wiki llvm-mos has a vibrant community both officially and unofficially affiliated with it. The main official hub is our [https://discord.gg/D9gRm3aznh Discord]; drop by if you have questions or would like to contribute. There are also a number of other projects loosely affiliated with llvm-mos; here are links to a few. {| class="wikitable" |+ !Site !Description |- |[https://github.com/MEGA65/mega65-libc mega65-libc] |C library for the Mega65. More thorough platform-specific code than present for the Mega65 llvm-mos SDK. Works with CC65, Clang, and KickC. |- |[https://mysterymath.neocities.org mysterymath's blog] |Current llvm-mos maintainer's blog. llvm-mos internals discussed often at a very thorough level of detail. |- |[https://github.com/mrk-its/rust-mos rust-mos] |Rust frontend port based on llvm-mos. Usually trails somewhat behind llvm-mos's releases. |} [[Category:Main]] 40ade97b72d150f8b49509d8920bc1c61547a5ec Frequently asked questions 0 49 517 433 2024-02-11T00:25:29Z Mysterymath 3 Add an entry about 24-bit integers. wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234"::"R"(value)); } </syntaxhighlight>The <code>R</code> constraint on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] cbfee43ea48862a4d70e813d967886c9165d0d35 522 517 2024-03-04T13:41:03Z Wombat 9 Expanded comment on example and added internal link to inline assembly page wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constraint] on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] f06d4f6567a477e98561dfd34d69fe6cde9281df 523 522 2024-03-04T13:44:57Z Wombat 9 Syntax highlighting wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] 56b03366757b5767ed09c66ffd0ea6a151012e8f 546 523 2024-08-27T15:29:22Z Wombat 9 Add comment to code snippet wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] 7571e550d4a228029dccbd5d4774b3792a2e4591 547 546 2024-08-27T15:32:30Z Wombat 9 /* Rule 2: Inputs, outputs, and volatile. */ wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly behaving so strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` and input operand } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] 60f50116d7c1bf063c0e07c79d4d6748102a51ef 550 547 2024-10-22T20:12:51Z Jbyrd 1 Warn about register modifiers wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly crashing the compiler or behaving strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` and input operand } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. Rule 4. Suspect your register modifiers. Register modifiers must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] 2e1a3cf62bdff89f949b8935415378d145e2714a 551 550 2024-10-22T20:22:39Z Jbyrd 1 Added link to assembly constraint violation information wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly crashing the compiler or behaving strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` and input operand } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. === Rule 4. Suspect your register modifiers. === Register modifiers must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] ed59fd1e40ee77e96db038f7dd51e65edea9b9ce 552 551 2024-10-22T20:23:20Z Jbyrd 1 /* Rule 4. Suspect your register modifiers. */ wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly crashing the compiler or behaving strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` and input operand } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. === Rule 4. Suspect your register constraints. === Register constraints must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] 18d299e1de340d486996ae90352a63401e5dca28 553 552 2024-10-22T20:24:11Z Jbyrd 1 /* Rule 4. Suspect your register constraints. */ wikitext text/x-wiki == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Why is inline assembly crashing the compiler or behaving strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` and input operand } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. === Rule 4. Suspect your register constraints. === [[C Inline Assembly|Register constraints]] must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] d8bc0a164edc61235bc23ebb292dbde5b7c0a39b 561 553 2025-02-28T19:40:01Z Jbyrd 1 Change ordering of FAQs wikitext text/x-wiki == Why is inline assembly crashing the compiler or behaving strangely? == If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think. The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though. === Rule 1: Declare your clobbers. === Take a look at the following example:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here"); return value; } </syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm"> foo: some compiler-unknown string of inline assembly goes here rts </syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what. There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.) So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c"> char foo(char value) { asm ("some compiler-unknown string of inline assembly goes here":::"a"); return value; } </syntaxhighlight><syntaxhighlight lang="asm"> foo: sta mos8(__rc2) some compiler-unknown string of inline assembly goes here lda mos8(__rc2) rts </syntaxhighlight> === Rule 2: Inputs, outputs, and <code>volatile</code>. === Here's a related example that also might not work:<syntaxhighlight lang="c"> void foo(char value) { asm ("sta 0x1234"); // bad: missing `volatile` and input operand } </syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop". In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile). The correct version of this snippet is:<syntaxhighlight lang="c"> void foo(char value) { asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value)); } </syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link]. === Rule 3: Consider using module-level asm or an assembly file. === As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job. Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines. There are broadly two ways to do this: module-level asm or dedicated assembly files. To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c"> asm ( ".text\n" ".global foo\n" "foo:\n" " sta 0x5678\n" " rts\n" ); void foo(char c); void bar(void) { foo(c); } </syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code. The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours. === Rule 4. Suspect your register constraints. === [[C Inline Assembly|Register constraints]] must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently. == Why is the compiler removing my infinite loops? == Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++] The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can. In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things. Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays. This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop. The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one. == Why is the compiler removing my memory accesses? == This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!". == Why isn't the linker placing my sections? == There are a two main reasons this can occur; the issue could be due to either or both. First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code> Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code> See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP. == Does llvm-mos support 24-bit integers? == Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect. We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK. [[Category:Main]] 7b2b8136f8439f963776de07a704f5f14d505718 Imaginary registers 0 42 518 477 2024-02-25T15:36:35Z 162.158.103.102 0 rewrite and clarify wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of memory locations reserved by the linker. (The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.) These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 1b6c36edc8a0a69c7ddbdb61578712276ed34a8a 532 518 2024-03-12T18:38:21Z Jbyrd 1 Clarification on consecutive zero page regions for imaginary registers wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of memory locations reserved by the linker. (The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.) These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume the __rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each __rs* register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 0cba0f14a0f3b602e5dc307ec39b4c6e04dd6045 533 532 2024-03-12T18:38:45Z Jbyrd 1 wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of memory locations reserved by the linker. (The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.) These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume the __rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each __rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 12a2c8a20f7f18d4c0d2903e9664b599e8f01408 534 533 2024-03-12T18:40:36Z Jbyrd 1 wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker. (The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.) These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume the __rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each __rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] 2936058a6fbab40b0fdd3d1751a3000d6e65c808 535 534 2024-03-12T18:42:13Z Jbyrd 1 wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker. (The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.) These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume the rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] f16b440c76b8ac2d3c730efc706d991cdaf370e5 558 535 2024-11-11T10:10:37Z Jbyrd 1 Add link to virtual register definition wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker. (The term ''imaginary registers'' is used because ''virtual registers'' have [https://llvm.org/docs/CodeGenerator.html#register-allocator another pre-defined meaning] in LLVM compiler development.) These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume the rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] a35f2f2c7b7f50338ed633a8d4125cdb420029a8 559 558 2024-11-11T10:14:08Z Jbyrd 1 More detail on register types wikitext text/x-wiki Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together? The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker. We use the term ''imaginary registers,'' because ''virtual registers'' have [https://llvm.org/docs/CodeGenerator.html#register-allocator another pre-defined meaning] in LLVM compiler development. These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Each ?c register contains a char type, which is 8 bits; each ?s register contains a short type, which is 16 bits. Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31. Note that LLVM-MOS does not assume the rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it. [[Category:Code generation]] [[Category:Assembly]] [[Category:C]] e8f7a8907cfebf3f98488c390ea4752be29665d6 Modifiers 0 16 529 483 2024-03-12T16:37:05Z 162.158.102.187 0 Add mos24() wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Description |- | <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address. |- | <code>mos24()</code> || Forces a symbol to be considered a 24-bit (long) address. |- | <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address. |} The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar). [[Category:Assembly]] 731a6817248d621c123d4da4ac4c3bc7c41c301f 541 529 2024-07-28T07:29:17Z Asie 12 update for v15.0.0 changes wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Short modifier !! Description |- | <code>mos8()</code> || <code><</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16()</code> || <code>!</code> || Forces a symbol to be considered a 16-bit (absolute) address. |- | <code>mos16lo()</code> || <code>#<</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> || <code>#></code> || The highest byte in the given 16-bit address. |- | <code>mos24()</code> || <code>></code> || Forces a symbol to be considered a 24-bit (long) address. |- | <code>mos24bank()</code> || <code>#^</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || || The highest byte in the segment of the given 24-bit address. |} The short modifiers follow the WDC standard as set out in the 65C816 datasheet. The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar). [[Category:Assembly]] c6914f1b0aee299ea04b88b3cad770596526354b 542 541 2024-07-28T07:29:51Z Asie 12 wikitext text/x-wiki When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address: LDA ''[the lowest byte in the 16-bit address of QSORT]'' However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well. In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address: LDA #mos16lo(QSORT) then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. Alternatively, a shorthand can be used in the form of: LDA #<QSORT The following modifiers are available for your use: {| class="wikitable sortable" |+ MOS Modifiers |- ! Modifier !! Short modifier !! Description |- | <code>mos8()</code> || <code><</code> || Forces a symbol to be considered an 8-bit (zero page) address. |- | <code>mos16()</code> || <code>!</code> || Forces a symbol to be considered a 16-bit (absolute) address. |- | <code>mos16lo()</code> || <code>#<</code> || The lowest byte in the given 16-bit address. |- | <code>mos16hi()</code> || <code>#></code> || The highest byte in the given 16-bit address. |- | <code>mos24()</code> || <code>></code> || Forces a symbol to be considered a 24-bit (long) address. |- | <code>mos24bank()</code> || <code>#^</code> || The 8-bit bank portion of the given 24-bit address. |- | <code>mos24segment()</code> || || The segment 16-bit portion of the given 24-bit address. |- | <code>mos24segmentlo()</code> || || The lowest byte in the segment of the given 24-bit address. |- | <code>mos24segmenthi()</code> || || The highest byte in the segment of the given 24-bit address. |} The short modifiers follow the WDC standard as set out in the 65C816 datasheet. The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values. All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address. The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar). [[Category:Assembly]] 11e6203e1f9a0282d99ead7426cc878ecac34867 ELF specification 0 15 530 482 2024-03-12T16:48:29Z Asie 12 0.3.1: Define mapping symbols. wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.1 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} === Mapping symbols === A given ELF section may contain instructions whose decoding or functional meaning is affected by CPU state. To allow utilities to process such sections appropriately, these sequences of instructions should be marked using mapping symbols. {| class="wikitable" |+ !Name !Type !Description |- |$ml |STT_NOTYPE |65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0. |- |$mh |STT_NOTYPE |65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1. |- |$xl |STT_NOTYPE |65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0. |- |$xh |STT_NOTYPE |65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] 3fb631cb60b25d5c9627988fb93a5db214bb68df 531 530 2024-03-12T16:49:12Z Asie 12 /* Mapping symbols */ wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.1 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} === Mapping symbols === A given ELF section may contain instructions whose decoding or functional meaning is affected by CPU state. To allow utilities to process such sections appropriately, these sequences of instructions should be marked using mapping symbols. {| class="wikitable" |+ !Prefix !Type !Description |- |$ml |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0. |- |$mh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1. |- |$xl |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0. |- |$xh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] f7852485334c43f1cde862040f4d4c909c1efdd1 536 531 2024-03-12T19:29:32Z Asie 12 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.1 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} === Mapping symbols === Mapping symbols are regular ELF symbols whose names are prefixed with a string of characters that begins with the symbol <code>$</code>. They mark the start of a sequence of instructions which are to be interpreted under the assumption of a particular processor mode or function, as denoted by the mapping symbol itself. This functionality is intended to allow utilities working with ELF files to appropriately decode and interpret sequences of instructions whose format or function is affected by CPU state. While various utilities may use mapping symbols in this way, they do not affect runtime behavior and serve only as hints. The complete list of mapping symbols defined by the specification is listed below: {| class="wikitable" |+ !Name prefix !Type !Description |- |$ml |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0. |- |$mh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1. |- |$xl |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0. |- |$xh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] 8e90c1741eb4c747d555c0f29a0d9377b41679ea 537 536 2024-06-20T23:10:09Z Jbyrd 1 Change description of R_MOS_PCREL_16 wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.1 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |A 16-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} === Mapping symbols === Mapping symbols are regular ELF symbols whose names are prefixed with a string of characters that begins with the symbol <code>$</code>. They mark the start of a sequence of instructions which are to be interpreted under the assumption of a particular processor mode or function, as denoted by the mapping symbol itself. This functionality is intended to allow utilities working with ELF files to appropriately decode and interpret sequences of instructions whose format or function is affected by CPU state. While various utilities may use mapping symbols in this way, they do not affect runtime behavior and serve only as hints. The complete list of mapping symbols defined by the specification is listed below: {| class="wikitable" |+ !Name prefix !Type !Description |- |$ml |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0. |- |$mh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1. |- |$xl |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0. |- |$xh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] c35876799637623fe1d3dc4c0e3db467e00e1a20 538 537 2024-06-28T19:48:22Z Jbyrd 1 Fixed 65ce02 link wikitext text/x-wiki == ELF specification for MOS-compatible processors == This is version 0.3.1 of this specification. Comments and improvements are solicited at johnwbyrd at gmail dot com. === Overview === This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] * [https://semver.org/ Semantic Versioning 2.0.0] === ELF header === The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space. Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed. Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool. === Machine type field === The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal). The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |- | EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- | EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one. |- | EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |- | EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65ce02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_HUC6280 |0x00000800 |HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |- |EM_MOS_65DTV02 |0x00001000 |Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one. |- |EM_MOS_4510 |0x00002000 |CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one. |- |EM_MOS_45GS02 |0x00004000 |45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one. |- |EM_MOS_SPC700 |0x00020000 |Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one. |} Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables. === Section flags === Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings. {| class="wikitable sortable" |+ e_flags bit fields |- ! Name !! Value !! Description |- | SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long. |} === Relocation types === Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.) The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution. {| class="wikitable" |+ !Name !Value !Description |- |R_MOS_NONE |0 |No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file. |- |R_MOS_IMM8 |1 |An 8-bit immediate value. |- |R_MOS_ADDR8 |2 |An 8-bit (e.g. zero page or direct page) address. |- |R_MOS_ADDR16 |3 |A 16-bit address. Standard for most MOS processors. |- |R_MOS_ADDR16_LO |4 |The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address. |- |R_MOS_ADDR16_HI |5 |The most significant byte of a 16-bit address. |- |R_MOS_PCREL_8 |6 |An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions. |- |R_MOS_ADDR24 |7 |A 24-bit address, used in the 65816 series. |- |R_MOS_ADDR24_BANK |8 |The 8-bit bank value from a 24-bit address. The most significant byte. |- |R_MOS_ADDR24_SEGMENT |9 |The 16-bit segment value from a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_LO |10 |The low byte from the segment value in a 24-bit address. |- |R_MOS_ADDR24_SEGMENT_HI |11 |The high byte from the segment value in a 24-bit address. |- |R_MOS_PCREL_16 |12 |A 16-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816. |- |R_MOS_FK_DATA_4 |13 |A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_FK_DATA_8 |14 |A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information. |- |R_MOS_ADDR_ASCIZ |15 |A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details. |- |R_MOS_IMM16 |16 |A 16-bit immediate value. |- |R_MOS_ADDR13 |17 |A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700. |- |} === Mapping symbols === Mapping symbols are regular ELF symbols whose names are prefixed with a string of characters that begins with the symbol <code>$</code>. They mark the start of a sequence of instructions which are to be interpreted under the assumption of a particular processor mode or function, as denoted by the mapping symbol itself. This functionality is intended to allow utilities working with ELF files to appropriately decode and interpret sequences of instructions whose format or function is affected by CPU state. While various utilities may use mapping symbols in this way, they do not affect runtime behavior and serve only as hints. The complete list of mapping symbols defined by the specification is listed below: {| class="wikitable" |+ !Name prefix !Type !Description |- |$ml |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0. |- |$mh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1. |- |$xl |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0. |- |$xh |STT_NOTYPE |65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1. |- |} == References == [https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference] [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976] [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] [http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference] [http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU] [https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki] [[Category:Linking]] 790d84143ff63e534a76853b9697df9bccb59212 Current status 0 25 539 308 2024-07-10T17:00:25Z MarkTheStrange 21 Clarify relative branch range. wikitext text/x-wiki == C compiler == * Code generation for the Commodore 64, Atari 800, and an included 6502 simulator. * The high and low-level optimizations expected of a young-ish LLVM backend ** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers ** The imaginary registers can be placed anywhere and need not be contiguous. ** The calling convention passes through registers whenever possible. ** Loop optimizations to select 6502 addressing modes ** Whole program "static stack" optimization *** Automatically identifies non-reentrant functions and allocates their frames as static globals *** Programs without recursion or complex function pointers may not need a soft stack at all. *** No manual annotations required ** Link time inlining and optimization across the whole program *** Includes SDK libraries. Library calls can be often optimized away completely! * Broad C99 and C++11 freestanding standards compatibility ** Interrupt handling ** C++ templates ** C++ virtual functions ** C++ new/delete ** C++ Run-Time Type Information (dynamic_cast, typeid) ** C++ static constructors/destructors (run before and after main) ** C++ "magic" function local static constructors/destructors * Excellent compiler usability ** Clang's world-class error messages ** IDE integration through the included custom clangd's Language Server Protocol ** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code> * A small standard library sufficient to provide the above and a few extras ** Simple printf ** Simple malloc/free ** exit, _Exit, and atexit === Notable Exceptions === * C99 requires supporting 64KiB locals * Hosted C with all the standard library bells and whistles. * Float/double * C++ Exceptions == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, where the usual signed-byte range of [-128,+127] applies to the address of the instruction _following_ the branch (the one executed next if the branch is skipped) rather than the branch instruction itself. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 786691b5b749d255316393b481faad8c52da57d7 540 539 2024-07-10T17:01:07Z MarkTheStrange 21 /* Assembler */ formatting wikitext text/x-wiki == C compiler == * Code generation for the Commodore 64, Atari 800, and an included 6502 simulator. * The high and low-level optimizations expected of a young-ish LLVM backend ** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers ** The imaginary registers can be placed anywhere and need not be contiguous. ** The calling convention passes through registers whenever possible. ** Loop optimizations to select 6502 addressing modes ** Whole program "static stack" optimization *** Automatically identifies non-reentrant functions and allocates their frames as static globals *** Programs without recursion or complex function pointers may not need a soft stack at all. *** No manual annotations required ** Link time inlining and optimization across the whole program *** Includes SDK libraries. Library calls can be often optimized away completely! * Broad C99 and C++11 freestanding standards compatibility ** Interrupt handling ** C++ templates ** C++ virtual functions ** C++ new/delete ** C++ Run-Time Type Information (dynamic_cast, typeid) ** C++ static constructors/destructors (run before and after main) ** C++ "magic" function local static constructors/destructors * Excellent compiler usability ** Clang's world-class error messages ** IDE integration through the included custom clangd's Language Server Protocol ** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code> * A small standard library sufficient to provide the above and a few extras ** Simple printf ** Simple malloc/free ** exit, _Exit, and atexit === Notable Exceptions === * C99 requires supporting 64KiB locals * Hosted C with all the standard library bells and whistles. * Float/double * C++ Exceptions == Assembler == The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time. The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, where the usual signed-byte range of [-128,+127] applies to the address of the instruction ''following'' the branch (the one executed next if the branch is skipped) rather than the branch instruction itself. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar. The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly. The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well. The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code. To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool. == Linker == The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines. The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables. If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project: <code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s llvm-objdump --all-headers --print-imm-hex -D %t.obj llvm-readelf --all %t.obj lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld llvm-readelf --all %t.elf llvm-objdump --all-headers --print-imm-hex -D %t.elf llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code> The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline. As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs. == ELF == Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files. Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables. == Deployment == If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page]. == Implementation == TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions.. For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS. [[Category:Main]] 05879598a91e4af94069b6ba7a4c97b81d035f7c C compiler 0 18 543 467 2024-08-01T13:25:16Z Wombat 9 Remove outdated statement about missing floating support. wikitext text/x-wiki A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang. Different CPUs may be selected using the <code>-mcpu</code> flag, for example <code>-mcpu=mosw65816</code>. == Defines == The compiler defines the <code>__mos__</code> macro for the <code>mos</code> target. In addition, defines are added for every CPU which is compatible with the code; for instance <code>-mcpu=mos65c02</code> additionally defines <code>__mos6502__</code> and <code>__mos65c02__</code>. == Known issues == The Clang is broadly compatible with the freestanding portion of the C99 standard. Some of the GCC and Clang language extensions work, some don't. Similarly some of the C++ language features work, some don't. We haven't exhaustively catalogued this; there's a lot, and some are extremely obscure and nearly useless. Still, some compiler extensions are de-facto standard, so we'll call out a few caveats for those: * The GCC/Clang alignment directives work only for global and static variables, not automatic (local) variables. Aligning the stack pointer is tricky, since it requires use of a frame pointer; but the rigors of 6502 indexing cause our frame pointer to be useless for this purpose. To get this to work, we may need *two* frame pointers. Expected call for this feature is low, so if you need it, let us know, and we'll reprioritize. [[Category:C]] dd4a6986cebe1be96844cbb9cbced1f16c0e13cd Zig 0 72 548 2024-09-23T12:33:27Z Kassane 22 zig-mos page wikitext text/x-wiki The [https://github.com/kassane/zig-mos-bootstrap zig-mos project] is based on llvm-mos and llvm-mos-sdk and allows Zig programs to run on 6502. If you want to use zig-mos, just get the available tarball (compatible with your operating system and computer architecture). If it's not available for your system, downloading bootstrap and running the build.(sh|bat) script on your local system to start building (with all dependencies already available) - no additional dependencies required! === Getting started === Once you've downloaded or built zig-mos, you can move on to the next step. It's also recommended to download [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], since the libraries and additional files aren't included in zig-mos. Let's test zig-mos by downloading the [https://github.com/kassane/zig-mos-examples zig-mos-examples] project. Having downloaded and extracted all the contents of the [https://github.com/kassane/zig-mos-examples zig-mos-examples] project, you can then select which project to build and later test using a specified emulator or device. === Commands === ==== Zig command line interface ==== * <code>build-lib</code>: build static-lib or shared-lib (add <code>-dynamic</code> flag); * <code>build-obj</code>: build object file, like <code>clang/gcc -c</code>. * <code>build-exe</code>: build executable * <code>build</code>: build-system mode, need <code>build.zig</code>. ==== Clang command line interface ==== * <code>zig cc</code>: clang CLI * <code>zig c++</code>: clang++ CLI (uses <code>llvm-libc++</code> + <code>llvm-libunwind</code> by default) '''Note:''' Zig toolchain does not change `libclang` codegen. However, the default config enable <code>-fsanitize=undefined</code>. ==== Targets available ==== <syntaxhighlight lang="bash"> $> zig build-obj --show-builtin -target mos-freestanding -mcpu= info: available CPUs for architecture 'mos': mos4510 mos45gs02 mos6502 mos6502x mos65c02 mos65ce02 mos65dtv02 mos65el02 moshuc6280 mosr65c02 mosspc700 mossweet16 mosw65816 mosw65c02 error: unknown CPU: '' </syntaxhighlight> '''Note:''' Freestanding targets are not listed on <code>zig targets | jq .libc</code> (triple-targets) ==== CPU Features ==== Similar to [Targets available](#targets-available) command, add <code>-mcpu</code> or <code>-Dcpu=</code> (need <code>build.zig</code>). * <code>+</code> add feature * <code>-</code> remove feature '''Note:''' For show feature list add <code>+</code>/<code>-</code> without feature name '''e.g.:''' <syntaxhighlight lang="bash"> $> zig build-obj --show-builtin -target mos-freestanding -mcpu=mos6502+ info: available CPU features for architecture 'mos': mos4510: CSG 4510 mos45gs02: 45GS02 mos6502: Generic MOS 6502, with support for BCD instructions mos6502x: NMOS 6502, with illegal opcode support mos65c02: Generic MOS 65C02 mos65ce02: Commodore 65CE02 mos65dtv02: The C64DTVs 6502 variant mos65el02: 65EL02 virtual machine mos_insns_4510: The new instructions present on CSG 4510 mos_insns_45gs02: The new instructions present on 45GS02 mos_insns_6502: The original documented 6502 instruction set mos_insns_6502bcd: BCD instruction support, including SED and CLD (most 6502 series CPUs support this) mos_insns_6502x: The 'illegal' opcodes present on some early variants of the original 6502 processor mos_insns_65c02: The new and modified instructions present on the generic 65c02 and variants mos_insns_65ce02: The new and modified instructions present on 65ce02 and variants mos_insns_65dtv02: The new and modified instructions present on the C64DTVs 6502 variant mos_insns_65el02: The new and modified instructions present on 65EL02 mos_insns_huc6280: The new and modified instructions present on HuC6280 mos_insns_r65c02: The new and modified instructions present on Rockwell and WDC 65c02 mos_insns_spc700: The SPC700 instruction set mos_insns_sweet16: The SWEET16 instruction set mos_insns_w65816: The new and modified instructions present on WDC 65816 mos_insns_w65c02: The new and modified instructions present on WDC 65c02 mos_long_register_names: Requires llvm_mos_* prefixes to all registers. Useful if your code has variable names that conflict with llvm-mos register names moshuc6280: Hudson Soft HuC6280 mosr65c02: Rockwell 65C02 mosspc700: Sony 6502-like CPUs, including the SPC700 mossweet16: MOS 6502 compatible with SWEET16 virtual machine support mosw65816: WDC 65816 mosw65c02: WDC 65C02 static_stack: Whether to use statically-allocated stack frames if possible. error: unknown CPU feature: '' </syntaxhighlight> ==== Target info (builtin) ==== '''Note:''' If like syntax-highlighting use <code>| bat -p -l zig</code> pipeline command or save this output as <code>builtin.zig</code> and open on your code editor. <syntaxhighlight lang="bash"> $> zig build-obj --show-builtin -target mos-freestanding </syntaxhighlight> <syntaxhighlight lang="zig"> const std = @import("std"); /// Zig version. When writing code that supports multiple versions of Zig, prefer /// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; pub const zig_version_string = "0.14.0-dev.mos.528+467e65f0c"; pub const zig_backend = std.builtin.CompilerBackend.stage2_llvm; pub const output_mode = std.builtin.OutputMode.Obj; pub const link_mode = std.builtin.LinkMode.static; pub const is_test = false; pub const single_threaded = false; pub const abi = std.Target.Abi.eabi; pub const cpu: std.Target.Cpu = .{ .arch = .mos, .model = &std.Target.mos.cpu.mos6502, .features = std.Target.mos.featureSet(&[_]std.Target.mos.Feature{ .mos6502, .mos_insns_6502, .mos_insns_6502bcd, .static_stack, }), }; pub const os = std.Target.Os{ .tag = .freestanding, .version_range = .{ .none = {} }, }; pub const target: std.Target = .{ .cpu = cpu, .os = os, .abi = abi, .ofmt = object_format, .dynamic_linker = std.Target.DynamicLinker.none, }; pub const object_format = std.Target.ObjectFormat.elf; pub const mode = std.builtin.OptimizeMode.Debug; pub const link_libc = false; pub const link_libcpp = false; pub const have_error_return_tracing = true; pub const valgrind_support = false; pub const sanitize_thread = false; pub const position_independent_code = false; pub const position_independent_executable = false; pub const strip_debug_info = false; pub const code_model = std.builtin.CodeModel.default; pub const omit_frame_pointer = false; </syntaxhighlight> === Other resources === *[https://github.com/zigtools/zls Zig language server]. 6352998e6b50b2e5116261e1ac7a4d5feb94e5c2 File:Zig mos neo6502.png 6 73 555 2024-11-05T17:32:57Z Kassane 22 wikitext text/x-wiki Using zig-mos on neo6502 emulator ce4e5e59eaa4f40611fe807c7bb3c2ae79fbde0b 556 555 2024-11-05T17:42:22Z Kassane 22 Kassane uploaded a new version of [[File:Zig mos neo6502.png]] wikitext text/x-wiki Using zig-mos on neo6502 emulator ce4e5e59eaa4f40611fe807c7bb3c2ae79fbde0b 557 556 2024-11-05T17:44:13Z Kassane 22 Kassane reverted [[File:Zig mos neo6502.png]] to an old version wikitext text/x-wiki Using zig-mos on neo6502 emulator ce4e5e59eaa4f40611fe807c7bb3c2ae79fbde0b Zero page 0 60 560 479 2025-01-02T16:26:03Z Asie 12 clarify __zp semantics for pointers wikitext text/x-wiki The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may want to reserve zero page memory from the compiler. Note that mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]]. The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page. For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage. Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can. User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>. Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using e.g. <code>char __zp foo;</code>or <code>char __zeropage foo;</code> . The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler, and pointers to such variables can also be declared to be in the zero page, which allows them to fit in 8 bits. For example, <code>char __zp *fptr = &foo; assert(sizeof(fptr) == 1);</code>. Note that <code>char __zp *fptr = &foo;</code> declares a pointer ''to'' the zero page (a pointer to ''char __zp'', which is itself not guaranteed to be in the zero page), while <code>char * __zp fptr = &foo;</code> declares a pointer ''in'' the zero page (a pointer in ''__zp'' to ''char''; the value pointed to can be in any memory location, so the pointer is two bytes, but the pointer itself is guaranteed to be in the zero page). While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program. [[Category:Code generation]] [[Category:C]] 73f811b03b0226c4731884629272af2fdf088aeb Cc65 integration 0 64 562 492 2025-04-21T21:06:15Z Mysterymath 3 Add LD65 banking information wikitext text/x-wiki LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section. This feature cannot be used during a relocatable link (<code>-r</code>), since the output is a relocatable ELF file, and ca65's relocations are much richer than LLVM-MOS ELF allows. The ld65 link step takes the second highest byte of LLD's 32-bit virtual addresses for ca65 segments and makes it the bank specifier, accessible with `.BANK`. The highest byte is zeroed. For symbols, the highest two bytes are zeroed. This is only done if no segment contains far or long addresses. ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names. The default cc65 segments are automatically assigned the correct type and flags. {| class="wikitable" |+Escape Sequences |- !Sequence!!Result |- |<code>__</code>||<code>_</code> |- |<code>_d</code>||<code>$</code> |- |<code>_h</code>||<code>-</code> |- |<code>_p</code>||<code>.</code> |- |<code>_xXX</code> |The byte given by hex string <code>XX</code> (case insensitive) |- |<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins) |- |<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins) |- |<code>_fw</code> |Section flag <code>SHF_WRITE</code> |- |<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code> |} [[Category:C]] [[Category:Linking]] 503316c98b59de865b8fb1b4fbb86474be0fe790 C calling convention 0 17 563 463 2025-05-27T22:13:08Z Jbyrd 1 Major rewrite based on lldb hacking session wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. - Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== - Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. - Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: `0x10 + (N * 2)` for `rcN`. Each RS register is assigned a DWARF register number: `0x210 + N` for `rsN`. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Design Rationale== This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions (see Davidson and Whalley, "Methods for Saving and Restoring Register Values across Function Calls," Software—Practice and Experience, Vol 21(2), 149-165, February 1991). The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. ===Examples=== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write through ''implicit'' 1st arg pointer |} ===References=== Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls. Software—Practice and Experience, 21(2), 149-165. RISC-V Calling Convention (for design inspiration) 44dcce922fef3da3eeb60109e57639394c7b5274 564 563 2025-05-27T22:18:20Z Jbyrd 1 Formatting cleanup wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. - Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN. Each RS register is assigned a DWARF register number: 0x210 + N` for rsN. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Examples== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write through ''implicit'' 1st arg pointer |} ===References=== [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165. [https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration) 025dfbd31c3d833f94cf43c7e9299657ac2ac5cc 565 564 2025-05-27T22:18:46Z Jbyrd 1 /* Return Values */ wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN. Each RS register is assigned a DWARF register number: 0x210 + N` for rsN. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Examples== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write through ''implicit'' 1st arg pointer |} ===References=== [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165. [https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration) 0a8e2bb19f8ccdb22fee1cf41ad95cb5528e642f 566 565 2025-05-28T22:29:20Z Mysterymath 3 /* Stack and Frame Pointer Usage */ wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN. Each RS register is assigned a DWARF register number: 0x210 + N` for rsN. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Examples== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write through ''implicit'' 1st arg pointer |} ===References=== [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165. [https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration) 3291ddf506f3ed9b60816541b1dbc793336b94e9 567 566 2025-05-28T22:30:49Z Mysterymath 3 Add details for frame pointer usage. wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging, stack unwinding, and certain kinds of C99 variable-sized arrays. Use of the frame pointer is affected by `-f[no-]emit-frame-pointer`, and the default is to not emit a frame pointer. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN. Each RS register is assigned a DWARF register number: 0x210 + N` for rsN. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Examples== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even very... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...long ones (incl. <code>rc6-7</code>) |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code> |...imaginary register pairs |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code> |Assign from left to right |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || || |By members if <code>sizeof</code> ≤ 4 |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || || |By pointer if <code>sizeof</code> > 4 |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | |<code>A, X, rc2-3</code> |Return members |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | |Return via write through ''implicit'' 1st arg pointer |} ===References=== [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165. [https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration) e085ca556121643ad587f9f9e018dc0d012377da 568 567 2025-05-28T22:34:53Z Mysterymath 3 Clarify example table. wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging, stack unwinding, and certain kinds of C99 variable-sized arrays. Use of the frame pointer is affected by `-f[no-]emit-frame-pointer`, and the default is to not emit a frame pointer. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN. Each RS register is assigned a DWARF register number: 0x210 + N` for rsN. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Examples== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code> !<code>rc6</code> !<code>rc7</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || | | |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code> | | ||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...very long ones. |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || | | |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code> | | ||<code>A</code>, <code>X</code> |...imaginary register pairs. |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| | | ||<code>A</code>, <code>X</code> |Assign from left to right. |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || | | || |By members if <code>sizeof</code> ≤ 4. |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || | | || |By pointer if <code>sizeof</code> > 4. |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | | | |<code>A, X, rc2-3</code> |Return members. |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | | | |Return via write through ''implicit'' 1st arg pointer. |} ===References=== [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165. [https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration) d6c0c51c0a3ad53db0a2547746abd4fe98455292 577 568 2025-07-28T20:09:03Z Jbyrd 1 /* DWARF Register Numbering */ wikitext text/x-wiki This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions. This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations. == Register Kinds and Their Purposes == Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers. Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I). Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers. === Caller-Saved and Callee-Saved Registers=== The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call. The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls. === Argument Passing === Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument. Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence. Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers. If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0). ==== Aggregate Types ==== Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value. Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void. ==== Variable Arguments ==== Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments. ==== Return Values ==== Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void. === Stack and Frame Pointer Usage === The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments. The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values. The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging, stack unwinding, and certain kinds of C99 variable-sized arrays. Use of the frame pointer is affected by `-f[no-]emit-frame-pointer`, and the default is to not emit a frame pointer. ==DWARF Register Numbering== Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN. Each RS register is assigned a DWARF register number: 0x210 + N for rsN. The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this] ==Examples== The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers. [[Category:C]] {| class="wikitable" |- !Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code> !<code>rc6</code> !<code>rc7</code>!!Output !Comment |- |<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || || | | |<code>A</code> |Numeric args passed... |- |<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code> | | ||<code>A</code>, <code>X</code>, <code>rc2-3</code> |...by value. Even... |- |<source lang="c" enclose="none">void f(int64_t a)</source> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> |<code>a</code> | |...very long ones. |- |<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| || | | |<code>rc2-3</code> |Pointers only through... |- |<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code> | | ||<code>A</code>, <code>X</code> |...imaginary register pairs. |- |<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| | | ||<code>A</code>, <code>X</code> |Assign from left to right. |- |<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || | | || |By members if <code>sizeof</code> ≤ 4. |- |<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || | | || |By pointer if <code>sizeof</code> > 4. |- |<source lang="c" enclose="none">div_t f(void *a)</source> | | |<code>a</code> |<code>a</code> | | | | |<code>A, X, rc2-3</code> |Return members. |- |<source lang="c" enclose="none">ldiv_t f(void *a)</source> | | |👻 |👻 |<code>a</code> |<code>a</code> | | | |Return via write through ''implicit'' 1st arg pointer. |} ===References=== [https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165. [https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration) 27d25ddf5c78566f3766b1407537b2ecd9097d5c File:Linux-c64.jpg 6 74 569 2025-07-27T19:02:51Z Jbyrd 1 wikitext text/x-wiki Linux booting on a Commodore 64 with REU 5040e3b881cc5ae23671b335e58f6affc0462f6c Welcome 0 1 570 513 2025-07-27T19:04:30Z Jbyrd 1 Add Linux on C64 wikitext text/x-wiki [[Category:Main]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 640b4b75be1439ac9caf3340bb53da93ab4e90d4 572 570 2025-07-27T19:10:04Z Jbyrd 1 Add game GIF wikitext text/x-wiki [[Category:Main]] [[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] ==Welcome to the llvm-mos project!== [[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 4a0c40ed714e8706acaacf866d312688f802b094 573 572 2025-07-28T19:22:45Z Jbyrd 1 Remove header to make mobile users happier wikitext text/x-wiki [[Category:Main]] [[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} c38e30e3c7dfdf1eb16bb216ad72a5df85283dd8 574 573 2025-07-28T19:34:05Z Jbyrd 1 Rewrote first paragraph wikitext text/x-wiki [[Category:Main]] [[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables software development for many classic platforms. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} bc498d4ca310772bb94645d2908a63e77e8179c1 575 574 2025-07-28T19:35:11Z Jbyrd 1 Tweak content wikitext text/x-wiki [[Category:Main]] [[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables software development for many classic platforms. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners GitHub runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 91cdbe71f670e4200b5e3d18849034bbaf48edc2 576 575 2025-07-28T19:35:44Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables modern software development for many classic platforms. Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners GitHub runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} 83901a6b5207d8cf4111a04faa56eea3b982154d 587 576 2025-10-02T15:25:05Z Jbyrd 1 wikitext text/x-wiki [[Category:Main]] [[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]] [[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]] [[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]] [[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]] [[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]] [[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]] [[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]] [[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]] llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables modern software development for many classic platforms. You can play with the compiler right now on [https://godbolt.org/z/c11Th3oMW Godbolt]! Here's a [https://godbolt.org/z/c11Th3oMW Commodore 64 project] that shows the C compiler in action. Try the "Click here to emulate" link on that page, to watch a Commodore 64 compute a hundred digits of pi. To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started]. Our work encompasses the following: * An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles.  Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers.  Our compiler includes, but is not limited to: ** broad C99 and C++11 freestanding compatibility ** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support] ** first-class [[Assembler|integrated 65xx assembler support]] ** clang's world-class error messages * An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers.  It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output.  Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included.  We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator].  The list of supported targets is growing rapidly. * [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries. * An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure.  We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners GitHub runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests].  We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure. Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now]. ====Notice==== The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement. ===Category tree=== <categorytree mode="pages" depth="3" hideroot="on">Main</categorytree> ===Categories=== {{Special:AllPages|namespace=14}} ===Pages=== {{Special:AllPages}} bad00805f8411912c0d9d83a98b42d82885941fa File:Miroh Jr for NES.gif 6 75 571 2025-07-27T19:09:11Z Jbyrd 1 wikitext text/x-wiki Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS 82ad901bc112134cb69d0fa6da89f0a0fce63fc5 DWARF specification 0 76 578 2025-09-28T07:52:52Z Jbyrd 1 Initial implementation. wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = '''Version 0.1.0''' Comments and improvements are solicited at johnwbyrd at gmail dot com. <span id="summary-for-implementers"></span> == Summary for Implementers == DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios * Are implementing comprehensive debugging support for the broader 6502 ecosystem The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. <span id="overview"></span> == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] <span id="terminology"></span> == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor Register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280’s memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF. '''Chipset Register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB Subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. <span id="register-number-format"></span> == Register Number Format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. <span id="processor-registers-vs.-chipset-registers"></span> === Processor Registers vs. Chipset Registers === The fundamental distinction in this specification is between processor registers and chipset registers: '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU’s architectural state. <span id="processor-register-formats-0x00000000-0x7fffffff"></span> === Processor Register Formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. <span id="structured-format"></span> ==== Structured Format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat Format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} <span id="chipset-registers-0x80000000-0xffffffff"></span> === Chipset Registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} <span id="format-selection-by-family"></span> === Format Selection by Family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} <span id="core-mos-processor-register-assignments"></span> == Core MOS Processor Register Assignments == <span id="family-0x00-mos-6502-family"></span> === Family 0x00: MOS 6502 Family === The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion. <span id="type-0x00-physical-registers"></span> ==== Type 0x00: Physical Registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} <span id="type-0x01-reserved"></span> ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. <span id="type-0x02-rc-imaginary-registers-8-bit"></span> ==== Type 0x02: RC Imaginary Registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} <span id="type-0x03-rs-imaginary-registers-16-bit"></span> ==== Type 0x03: RS Imaginary Registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} <span id="type-0x04-rl-imaginary-registers-32-bit"></span> ==== Type 0x04: RL Imaginary Registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} <span id="type-0x05-rq-imaginary-registers-64-bit"></span> ==== Type 0x05: RQ Imaginary Registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} <span id="types-0x06-0x7f-reserved"></span> ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. <span id="type-0x10-huc6280-extensions"></span> ==== Type 0x10: HuC6280 Extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} <span id="type-0x11-65108502-extensions"></span> ==== Type 0x11: 6510/8502 Extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} <span id="type-0x12-ricoh-2a032a07-extensions"></span> ==== Type 0x12: Ricoh 2A03/2A07 Extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} <span id="type-0x13-65ce02-extensions"></span> ==== Type 0x13: 65CE02 Extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} <span id="type-0x14-w65c02s-extensions"></span> ==== Type 0x14: W65C02S Extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). <span id="types-0x15-0x7f-reserved"></span> ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. <span id="types-0x80-0xff-banking-and-memory-management"></span> ==== Types 0x80-0xFF: Banking and Memory Management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. <span id="family-0x01-65816-native-mode"></span> === Family 0x01: 65816 Native Mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. <span id="type-0x00-native-mode-registers"></span> ==== Type 0x00: Native Mode Registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} <span id="family-0x02-virtual-machines"></span> === Family 0x02: Virtual Machines === Virtual machines that run on MOS processors receive family identifier 0x02. <span id="type-0x00-sweet-16"></span> ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. <span id="type-0x01-reserved-for-bbc-vm"></span> ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. <span id="types-0x02-0xff-reserved"></span> ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. <span id="families-0x03-0x7f-reserved"></span> === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. <span id="family-0x03-spc700"></span> == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. <span id="type-0x00-core-registers"></span> ==== Type 0x00: Core Registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} <span id="chipset-register-assignments"></span> == Chipset Register Assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform Organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic Chipset Platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O Controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common Sound Processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common Video Controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-Specific Chipset Platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended Register Concepts == <span id="banking-support"></span> === Banking Support === Memory banking schemes receive their own type identifiers within a family’s allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB Subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation Notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM’s initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 60edaa91b7b19f11bd9e13176e7b818bbab232da 579 578 2025-09-28T07:53:58Z Jbyrd 1 /* DWARF Specification for MOS-Compatible Processors */ wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = '''Version 0.1.0''' Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for Implementers == DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios * Are implementing comprehensive debugging support for the broader 6502 ecosystem The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. <span id="overview"></span> == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] <span id="terminology"></span> == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor Register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280’s memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF. '''Chipset Register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB Subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. <span id="register-number-format"></span> == Register Number Format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. <span id="processor-registers-vs.-chipset-registers"></span> === Processor Registers vs. Chipset Registers === The fundamental distinction in this specification is between processor registers and chipset registers: '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU’s architectural state. <span id="processor-register-formats-0x00000000-0x7fffffff"></span> === Processor Register Formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. <span id="structured-format"></span> ==== Structured Format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat Format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} <span id="chipset-registers-0x80000000-0xffffffff"></span> === Chipset Registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} <span id="format-selection-by-family"></span> === Format Selection by Family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} <span id="core-mos-processor-register-assignments"></span> == Core MOS Processor Register Assignments == <span id="family-0x00-mos-6502-family"></span> === Family 0x00: MOS 6502 Family === The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion. <span id="type-0x00-physical-registers"></span> ==== Type 0x00: Physical Registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} <span id="type-0x01-reserved"></span> ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. <span id="type-0x02-rc-imaginary-registers-8-bit"></span> ==== Type 0x02: RC Imaginary Registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} <span id="type-0x03-rs-imaginary-registers-16-bit"></span> ==== Type 0x03: RS Imaginary Registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} <span id="type-0x04-rl-imaginary-registers-32-bit"></span> ==== Type 0x04: RL Imaginary Registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} <span id="type-0x05-rq-imaginary-registers-64-bit"></span> ==== Type 0x05: RQ Imaginary Registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} <span id="types-0x06-0x7f-reserved"></span> ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. <span id="type-0x10-huc6280-extensions"></span> ==== Type 0x10: HuC6280 Extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} <span id="type-0x11-65108502-extensions"></span> ==== Type 0x11: 6510/8502 Extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} <span id="type-0x12-ricoh-2a032a07-extensions"></span> ==== Type 0x12: Ricoh 2A03/2A07 Extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} <span id="type-0x13-65ce02-extensions"></span> ==== Type 0x13: 65CE02 Extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} <span id="type-0x14-w65c02s-extensions"></span> ==== Type 0x14: W65C02S Extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). <span id="types-0x15-0x7f-reserved"></span> ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. <span id="types-0x80-0xff-banking-and-memory-management"></span> ==== Types 0x80-0xFF: Banking and Memory Management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. <span id="family-0x01-65816-native-mode"></span> === Family 0x01: 65816 Native Mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. <span id="type-0x00-native-mode-registers"></span> ==== Type 0x00: Native Mode Registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} <span id="family-0x02-virtual-machines"></span> === Family 0x02: Virtual Machines === Virtual machines that run on MOS processors receive family identifier 0x02. <span id="type-0x00-sweet-16"></span> ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. <span id="type-0x01-reserved-for-bbc-vm"></span> ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. <span id="types-0x02-0xff-reserved"></span> ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. <span id="families-0x03-0x7f-reserved"></span> === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. <span id="family-0x03-spc700"></span> == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. <span id="type-0x00-core-registers"></span> ==== Type 0x00: Core Registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} <span id="chipset-register-assignments"></span> == Chipset Register Assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform Organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic Chipset Platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O Controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common Sound Processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common Video Controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-Specific Chipset Platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended Register Concepts == <span id="banking-support"></span> === Banking Support === Memory banking schemes receive their own type identifiers within a family’s allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB Subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation Notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM’s initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 852a09fcb1acd28affb6fc404604e29951f18f49 580 579 2025-09-28T07:58:20Z Jbyrd 1 /* DWARF Specification for MOS-Compatible Processors */ wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = Version 0.1.0 Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for implementers == DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios * Are implementing comprehensive debugging support for the broader 6502 ecosystem The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor Register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280’s memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF. '''Chipset Register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB Subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. == Register number format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. === Processor registers vs. chipset registers === The fundamental distinction in this specification is between processor registers and chipset registers: '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU’s architectural state. === Processor register formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. ==== Structured format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat Format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} === Chipset registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} === Format selection by family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} == Core MOS processor register assignments == === Family 0x00: MOS 6502 family === The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion. ==== Type 0x00: Physical registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. ==== Type 0x02: RC imaginary registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} ==== Type 0x03: RS imaginary registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} ==== Type 0x04: RL imaginary registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} ==== Type 0x05: RQ imaginary registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. ==== Type 0x10: HuC6280 extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} ==== Type 0x11: 6510/8502 extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} ==== Type 0x12: Ricoh 2A03/2A07 extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} ==== Type 0x13: 65CE02 extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} ==== Type 0x14: W65C02S extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. ==== Types 0x80-0xFF: Banking and memory management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. === Family 0x01: 65816 native mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. ==== Type 0x00: Native mode registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} === Family 0x02: Virtual machines === Virtual machines that run on MOS processors receive family identifier 0x02. ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. ==== Type 0x00: Core Registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} == Chipset register assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform Organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic Chipset Platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O Controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common Sound Processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common Video Controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-Specific Chipset Platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo Systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended Register Concepts == <span id="banking-support"></span> === Banking Support === Memory banking schemes receive their own type identifiers within a family’s allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB Subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation Notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM’s initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 477bd5bea57279f8ad2800866f5bd3038adadc06 581 580 2025-09-28T08:02:24Z Jbyrd 1 Reformat to Sentence case wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = Version 0.1.0 Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for implementers == DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios * Are implementing comprehensive debugging support for the broader 6502 ecosystem The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF. '''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. == Register number format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. === Processor registers vs. chipset registers === The fundamental distinction in this specification is between processor registers and chipset registers: '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state. === Processor register formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. ==== Structured format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} === Chipset registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} === Format selection by family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} == Core MOS processor register assignments == === Family 0x00: MOS 6502 family === The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion. ==== Type 0x00: Physical registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. ==== Type 0x02: RC imaginary registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} ==== Type 0x03: RS imaginary registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} ==== Type 0x04: RL imaginary registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} ==== Type 0x05: RQ imaginary registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. ==== Type 0x10: HuC6280 extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} ==== Type 0x11: 6510/8502 extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} ==== Type 0x12: Ricoh 2A03/2A07 extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} ==== Type 0x13: 65CE02 extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} ==== Type 0x14: W65C02S extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. ==== Types 0x80-0xFF: Banking and memory management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. === Family 0x01: 65816 native mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. ==== Type 0x00: Native mode registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} === Family 0x02: Virtual machines === Virtual machines that run on MOS processors receive family identifier 0x02. ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. ==== Type 0x00: Core registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} == Chipset register assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic chipset platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common sound processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common video controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-specific chipset platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended register concepts == <span id="banking-support"></span> === Banking support === Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 7476c8a99c557b29d94b1e0dda3d63e580dc54ef 583 581 2025-09-28T08:13:29Z Jbyrd 1 Lots of links added wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = Version 0.1.0 Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for implementers == [[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF. '''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. == Register number format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. === Processor registers vs. chipset registers === The fundamental distinction in this specification is between processor registers and chipset registers: '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state. === Processor register formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. ==== Structured format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} === Chipset registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} === Format selection by family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} == Core MOS processor register assignments == === Family 0x00: MOS 6502 family === The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion. ==== Type 0x00: Physical registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. ==== Type 0x02: RC imaginary registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} ==== Type 0x03: RS imaginary registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} ==== Type 0x04: RL imaginary registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} ==== Type 0x05: RQ imaginary registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. ==== Type 0x10: HuC6280 extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} ==== Type 0x11: 6510/8502 extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} ==== Type 0x12: Ricoh 2A03/2A07 extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} ==== Type 0x13: 65CE02 extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} ==== Type 0x14: W65C02S extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. ==== Types 0x80-0xFF: Banking and memory management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. === Family 0x01: 65816 native mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. ==== Type 0x00: Native mode registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} === Family 0x02: Virtual machines === Virtual machines that run on MOS processors receive family identifier 0x02. ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. ==== Type 0x00: Core registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} == Chipset register assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic chipset platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common sound processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common video controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-specific chipset platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended register concepts == <span id="banking-support"></span> === Banking support === Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 92e62009d7a20e4aebfe94f12d4e0573e1e487fd 584 583 2025-09-28T08:16:13Z Jbyrd 1 Clearer description of DWARF numbering vs addresses wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = Version 0.1.0 Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for implementers == [[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy DWARF numbers 0x00000000-0x7FFFFFFF. '''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy DWARF numbers 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. == Register number format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state. === Processor register formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. ==== Structured format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} === Chipset registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} === Format selection by family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} == Core MOS processor register assignments == === Family 0x00: MOS 6502 family === The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion. ==== Type 0x00: Physical registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. ==== Type 0x02: RC imaginary registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} ==== Type 0x03: RS imaginary registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} ==== Type 0x04: RL imaginary registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} ==== Type 0x05: RQ imaginary registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. ==== Type 0x10: HuC6280 extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} ==== Type 0x11: 6510/8502 extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} ==== Type 0x12: Ricoh 2A03/2A07 extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} ==== Type 0x13: 65CE02 extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} ==== Type 0x14: W65C02S extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. ==== Types 0x80-0xFF: Banking and memory management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. === Family 0x01: 65816 native mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. ==== Type 0x00: Native mode registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} === Family 0x02: Virtual machines === Virtual machines that run on MOS processors receive family identifier 0x02. ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. ==== Type 0x00: Core registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} == Chipset register assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic chipset platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common sound processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common video controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-specific chipset platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended register concepts == <span id="banking-support"></span> === Banking support === Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 4f7c57560b855859a320593434b4bd7bfba24d26 585 584 2025-09-28T08:16:35Z Jbyrd 1 /* Family 0x00: MOS 6502 family */ wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = Version 0.1.0 Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for implementers == [[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy DWARF numbers 0x00000000-0x7FFFFFFF. '''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy DWARF numbers 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. == Register number format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state. === Processor register formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. ==== Structured format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} === Chipset registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} === Format selection by family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} == Core MOS processor register assignments == === Family 0x00: MOS 6502 family === The MOS 6502 family uses the structured format with multiple register types. Each type has space reserved for future expansion. ==== Type 0x00: Physical registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006 | S | Stack Register (LLVM-MOS) |- | 0x00000007 | D | Direct Page Register (65816 emulation) |- | 0x00000008-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. ==== Type 0x02: RC imaginary registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} ==== Type 0x03: RS imaginary registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} ==== Type 0x04: RL imaginary registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} ==== Type 0x05: RQ imaginary registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. ==== Type 0x10: HuC6280 extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} ==== Type 0x11: 6510/8502 extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} ==== Type 0x12: Ricoh 2A03/2A07 extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} ==== Type 0x13: 65CE02 extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} ==== Type 0x14: W65C02S extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. ==== Types 0x80-0xFF: Banking and memory management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. === Family 0x01: 65816 native mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. ==== Type 0x00: Native mode registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} === Family 0x02: Virtual machines === Virtual machines that run on MOS processors receive family identifier 0x02. ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. ==== Type 0x00: Core registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} == Chipset register assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic chipset platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common sound processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common video controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-specific chipset platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended register concepts == <span id="banking-support"></span> === Banking support === Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] e0d88e287660597b1d7352c111607d65068bb7e7 586 585 2025-09-28T08:21:58Z Jbyrd 1 /* Type 0x00: Physical registers */ wikitext text/x-wiki <span id="dwarf-specification-for-mos-compatible-processors"></span> = DWARF Specification for MOS-Compatible Processors = Version 0.1.0 Comments and improvements are solicited at johnwbyrd at gmail dot com. == Summary for implementers == [[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult. Most 6502 debugger and emulator developers only need these six DWARF register numbers: {| class="wikitable" |- ! DWARF Number ! Register ! Description |- | 0 | A | Accumulator |- | 1 | X | X Index Register |- | 2 | Y | Y Index Register |- | 3 | P | Processor Status Register |- | 4 | SP | Stack Pointer |- | 5 | PC | Program Counter |} These numbers are compatible with MAME and most existing 6502 debugging tools. '''You should read the full specification if you:''' * Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing * Are implementing LLVM-MOS or another compiler that uses imaginary registers * Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02) * Are working with NES mappers or other chipset-specific registers * Want to avoid register number collisions in complex debugging scenarios The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools. == Overview == This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem. This specification considers the following documents to be normative, and incorporates them by reference: * [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels] * [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5] * [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors] * [https://semver.org/ Semantic Versioning 2.0.0] == Terminology == This specification uses specific terminology to organize the DWARF register number space: '''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy DWARF numbers 0x00000000-0x7FFFFFFF. '''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy DWARF numbers 0x80000000-0xFFFFFFFF and are organized by platform and specific chip. '''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number). '''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format. '''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips. '''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms. '''Register''': An individual register within its containing type or chipset. '''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register. == Register number format == DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register. '''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution. '''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state. === Processor register formats (0x00000000-0x7FFFFFFF) === The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format. ==== Structured format ==== Used when a processor family needs to organize registers into distinct types: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-16 | Type | Register type within family | 0x00-0xFF (256 types) |- | 15-0 | Register | Register number within type | 0x0000-0xFFFF (65,536 registers) |} <span id="flat-format"></span> ==== Flat format ==== Used when a processor family needs a simple, large register space: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31-24 | Family | Processor architecture family | 0x00-0x7F (128 families) |- | 23-0 | Register | Register number | 0x000000-0xFFFFFF (16,777,216 registers) |} === Chipset registers (0x80000000-0xFFFFFFFF) === Chipset registers always use a structured format: {| class="wikitable" |- ! Bits ! Field ! Description ! Range |- | 31 | Chipset Flag | Always 1 | Identifies chipset space |- | 30-24 | Platform | Computer platform | 0x00-0x7F (128 platforms) |- | 23-16 | Chipset | Chip within platform | 0x00-0xFF (256 chips) |- | 15-0 | Register | Register within chipset | 0x0000-0xFFFF (65,536 registers) |} === Format selection by family === Each processor family defines its register format: {| class="wikitable" |- ! Family ! Name ! Format ! Rationale |- | 0x00 | MOS 6502 | Structured | Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ) |- | 0x01 | 65816 Native | Structured | Distinct types for native mode features and banking |- | 0x02 | Virtual Machines | Structured | Different VMs as different types |- | 0x03 | SPC700 | Structured | Different architecture with PSW and YA register |- | 0x04-0x7F | Reserved | Family-defined | Future families declare their format |} == Core MOS processor register assignments == === Family 0x00: MOS 6502 family === The MOS 6502 family uses the structured format with multiple register types. Each type has space reserved for future expansion. ==== Type 0x00: Physical registers ==== Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00000000 | A | Accumulator |- | 0x00000001 | X | X Index Register |- | 0x00000002 | Y | Y Index Register |- | 0x00000003 | P | Processor Status Register |- | 0x00000004 | SP | Stack Pointer |- | 0x00000005 | PC | Program Counter |- | 0x00000006-0x000000FF | - | Reserved for future physical registers |- | 0x00000100 | ALSB | Accumulator LSB |- | 0x00000101 | XLSB | X Index LSB |- | 0x00000102 | YLSB | Y Index LSB |- | 0x00000103 | SLSB | Stack Register LSB |- | 0x00000104-0x000001FF | - | Reserved for future LSB registers |- | 0x00000200 | C | Carry Flag |- | 0x00000201 | Z | Zero Flag |- | 0x00000202 | I | Interrupt Disable Flag |- | 0x00000203 | D | Decimal Mode Flag |- | 0x00000204 | B | Break Flag |- | 0x00000205 | V | Overflow Flag |- | 0x00000206 | N | Negative Flag |- | 0x00000207 | NZ | Combined N and Z flags |- | 0x00000208-0x000002FF | - | Reserved for future flag registers |- | 0x00000300 | Fake | Pseudo-register for unsupported types |- | 0x00000301-0x00000FFF | - | Reserved for future special registers |- | 0x00001000-0x0000FFFF | - | Reserved for future physical extensions |} ==== Type 0x01: Reserved ==== The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use. ==== Type 0x02: RC imaginary registers (8-bit) ==== LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00020000-0x000200FF | RC0-RC255 | 256 8-bit imaginary registers |- | 0x00020100-0x000201FF | - | Reserved |- | 0x00021000-0x000210FF | RC0LSB-RC255LSB | LSB subregisters for RC registers |- | 0x00021100-0x0002FFFF | - | Reserved for future RC expansion |} ==== Type 0x03: RS imaginary registers (16-bit) ==== Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00030000-0x0003007F | RS0-RS127 | 128 16-bit imaginary registers |- | 0x00030080-0x0003FFFF | - | Reserved for future RS expansion |} ==== Type 0x04: RL imaginary registers (32-bit) ==== Thirty-two-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00040000-0x0004001F | RL0-RL31 | 32 32-bit imaginary registers |- | 0x00040020-0x000400FF | - | Reserved |- | 0x00041000-0x000410FF | RL0LSB-RL31LSB | LSB subregisters for RL registers |- | 0x00041100-0x0004FFFF | - | Reserved for future RL expansion |} ==== Type 0x05: RQ imaginary registers (64-bit) ==== Sixty-four-bit imaginary registers for future expansion. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00050000-0x0005000F | RQ0-RQ15 | 16 64-bit imaginary registers |- | 0x00050010-0x000500FF | - | Reserved |- | 0x00051000-0x0005100F | RQ0LSB-RQ15LSB | LSB subregisters for RQ registers |- | 0x00051010-0x0005FFFF | - | Reserved for future RQ expansion |} ==== Types 0x06-0x7F: Reserved ==== Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions. ==== Type 0x10: HuC6280 extensions ==== The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00100000 | MPR0 | Memory Page Register 0 |- | 0x00100001 | MPR1 | Memory Page Register 1 |- | 0x00100002 | MPR2 | Memory Page Register 2 |- | 0x00100003 | MPR3 | Memory Page Register 3 |- | 0x00100004 | MPR4 | Memory Page Register 4 |- | 0x00100005 | MPR5 | Memory Page Register 5 |- | 0x00100006 | MPR6 | Memory Page Register 6 |- | 0x00100007 | MPR7 | Memory Page Register 7 |- | 0x00100008 | TIMER_CNT | Timer Counter |- | 0x00100009 | TIMER_CTRL | Timer Control |- | 0x0010000A-0x0010FFFF | - | Reserved |} ==== Type 0x11: 6510/8502 extensions ==== The 6510 (C64) and 8502 (C128) include integrated I/O port functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00110000 | DDR | Data Direction Register (location 0x00) |- | 0x00110001 | PORT | I/O Port Register (location 0x01) |- | 0x00110002-0x0011FFFF | - | Reserved |} ==== Type 0x12: Ricoh 2A03/2A07 extensions ==== The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00120000 | OAMDMA | OAM DMA Register |- | 0x00120001-0x0012FFFF | - | Reserved |} ==== Type 0x13: 65CE02 extensions ==== The 65CE02 (Commodore 65) extends the 65C02 with additional registers. {| class="wikitable" |- ! Address Range ! Registers ! Description |- | 0x00130000 | B | Base Page Register |- | 0x00130001 | Z | Z Index Register |- | 0x00130002 | SPH | Stack Pointer High |- | 0x00130003-0x0013FFFF | - | Reserved |} ==== Type 0x14: W65C02S extensions ==== Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF). ==== Types 0x15-0x7F: Reserved ==== Reserved for other 6502 variant extensions. ==== Types 0x80-0xFF: Banking and memory management ==== These types support banking schemes where each distinct banking configuration receives its own type identifier. === Family 0x01: 65816 native mode === The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. Whether these registers are punned to the underlying 8-bit registers in 6502 compatible mode is implementation defined. ==== Type 0x00: Native mode registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x01000000 | A | 16-bit Accumulator |- | 0x01000001 | X | 16-bit X Index |- | 0x01000002 | Y | 16-bit Y Index |- | 0x01000003 | S | 16-bit Stack Pointer |- | 0x01000004 | D | 16-bit Direct Page |- | 0x01000005 | PC | Program Counter |- | 0x01000006 | P | Processor Status |- | 0x01000007 | DBR | Data Bank Register |- | 0x01000008 | PBR | Program Bank Register |- | 0x01000009-0x01000FFF | - | Reserved |- | 0x01001000-0x0100FFFF | - | Reserved for future native mode extensions |} === Family 0x02: Virtual machines === Virtual machines that run on MOS processors receive family identifier 0x02. ==== Type 0x00: SWEET-16 ==== SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion. ==== Type 0x01: Reserved for BBC VM ==== The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions. ==== Types 0x02-0xFF: Reserved ==== Reserved for additional virtual machine implementations. === Families 0x03-0x7F: Reserved === These family identifiers are reserved for future MOS-compatible architectures and processor variants. == Family 0x03: SPC700 == The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation. ==== Type 0x00: Core registers ==== {| class="wikitable" |- ! Address ! Register ! Description |- | 0x03000000 | A | Accumulator |- | 0x03000001 | X | X Index Register |- | 0x03000002 | Y | Y Index Register |- | 0x03000003 | PSW | Program Status Word (not compatible with 6502 P register) |- | 0x03000004 | SP | Stack Pointer |- | 0x03000005 | PC | Program Counter |- | 0x03000006 | YA | Combined Y:A 16-bit register |- | 0x03000007-0x030000FF | - | Reserved |} == Chipset register assignments == Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers. <span id="platform-organization"></span> === Platform organization === {| class="wikitable" |- ! Platform Range ! Category ! Description |- | 0x00-0x0F | Generic | Chips used across multiple systems |- | 0x10-0x7F | System-Specific | Platform-specific chips |} <span id="generic-chipset-platforms"></span> === Generic chipset platforms === <span id="platform-0x00-common-io-controllers"></span> ==== Platform 0x00: Common I/O controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x80010000-0x8001FFFF | 6522 VIA |- | 0x02 | 0x80020000-0x8002FFFF | 6551 ACIA |- | 0x03 | 0x80030000-0x8003FFFF | 6526 CIA |- | 0x04 | 0x80040000-0x8004FFFF | 6532 RIOT |- | 0x05-0xFF | Reserved | Future I/O controllers |} <span id="platform-0x01-common-sound-processors"></span> ==== Platform 0x01: Common sound processors ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x81010000-0x8101FFFF | SID (6581/8580) |- | 0x02 | 0x81020000-0x8102FFFF | POKEY |- | 0x03 | 0x81030000-0x8103FFFF | AY-3-8910 |- | 0x04 | 0x81040000-0x8104FFFF | SN76489 |- | 0x05-0xFF | Reserved | Future sound processors |} <span id="platform-0x02-common-video-controllers"></span> ==== Platform 0x02: Common video controllers ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x82010000-0x8201FFFF | TMS9918 |- | 0x02 | 0x82020000-0x8202FFFF | MC6845 CRTC |- | 0x03-0xFF | Reserved | Future video controllers |} <span id="system-specific-chipset-platforms"></span> === System-specific chipset platforms === <span id="platform-0x10-commodore-systems"></span> ==== Platform 0x10: Commodore systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x90010000-0x9001FFFF | VIC-II |- | 0x02 | 0x90020000-0x9002FFFF | VIC (VIC-20) |- | 0x03 | 0x90030000-0x9003FFFF | TED (Plus/4) |- | 0x04 | 0x90040000-0x9004FFFF | VDC (C128) |} <span id="platform-0x11-apple-systems"></span> ==== Platform 0x11: Apple systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x91010000-0x9101FFFF | Apple II 74LS graphics |- | 0x02 | 0x91020000-0x9102FFFF | Disk II controller |- | 0x03 | 0x91030000-0x9103FFFF | Apple IIgs custom chips |} <span id="platform-0x12-atari-systems"></span> ==== Platform 0x12: Atari systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x92010000-0x9201FFFF | ANTIC |- | 0x02 | 0x92020000-0x9202FFFF | GTIA |- | 0x03 | 0x92030000-0x9203FFFF | FREDDIE |} <span id="platform-0x13-nintendo-systems"></span> ==== Platform 0x13: Nintendo systems ==== {| class="wikitable" |- ! Chipset ! Address Range ! Device |- | 0x01 | 0x93010000-0x9301FFFF | PPU |- | 0x02 | 0x93020000-0x9302FFFF | APU |- | 0x03 | 0x93030000-0x9303FFFF | NROM (No mapper) |- | 0x04 | 0x93040000-0x9304FFFF | MMC1 (SxROM) |- | 0x05 | 0x93050000-0x9305FFFF | UNROM |- | 0x06 | 0x93060000-0x9306FFFF | CNROM |- | 0x07 | 0x93070000-0x9307FFFF | MMC3 (TxROM) |- | 0x08 | 0x93080000-0x9308FFFF | AOROM |- | 0x09 | 0x93090000-0x9309FFFF | MMC5 (ExROM) |- | 0x0A | 0x930A0000-0x930AFFFF | MMC2 (PxROM) |- | 0x0B | 0x930B0000-0x930BFFFF | MMC4 (FxROM) |- | 0x0C | 0x930C0000-0x930CFFFF | ColorDreams |- | 0x0D | 0x930D0000-0x930DFFFF | CPROM |- | 0x0E | 0x930E0000-0x930EFFFF | GXROM |- | 0x0F | 0x930F0000-0x930FFFFF | GTROM |- | 0x10 | 0x93100000-0x9310FFFF | UNROM-512 |- | 0x11 | 0x93110000-0x9311FFFF | Action53 |- | 0x12 | 0x93120000-0x9312FFFF | AxROM |- | 0x13 | 0x93130000-0x9313FFFF | BNROM |- | 0x14 | 0x93140000-0x9314FFFF | Camerica |- | 0x15 | 0x93150000-0x9315FFFF | FME-7 (Sunsoft 5B) |- | 0x16 | 0x93160000-0x9316FFFF | VRC2 |- | 0x17 | 0x93170000-0x9317FFFF | VRC4 |- | 0x18 | 0x93180000-0x9318FFFF | VRC6 |- | 0x19 | 0x93190000-0x9319FFFF | VRC7 |- | 0x1A | 0x931A0000-0x931AFFFF | Namco 163 |- | 0x1B | 0x931B0000-0x931BFFFF | Namco 106/109/118/119 |- | 0x1C | 0x931C0000-0x931CFFFF | TQROM (MMC3 + CHR-RAM) |- | 0x1D-0xFF | Reserved | Future mapper variants |} <span id="extended-register-concepts"></span> == Extended register concepts == <span id="banking-support"></span> === Banking support === Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes. <span id="lsb-subregisters"></span> === LSB subregisters === LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers. <span id="implementation-notes"></span> == Implementation notes == The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used. The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management. <span id="rationale"></span> == Rationale == The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor. Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged. There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time. <span id="references"></span> == References == * [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual] * [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816] * [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository] * [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention] * [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation] * [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers] * [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64] * [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI] 5c3f343eb784b4e31a5e9d74adc255aa52045e02 DWARF overview 0 77 582 2025-09-28T08:07:23Z Jbyrd 1 Initial implementation wikitext text/x-wiki === What's DWARF? Why is it relevant to llvm-mos? === DWARF is a debugging data format that connects your source code to machine code. When you compile a C program with `-g`, the compiler embeds DWARF information alongside your executable. This data tells debuggers like GDB which source line corresponds to each machine instruction, where variables are stored, and how to map register contents back to your program's symbols. Modern debuggers rely entirely on DWARF to provide source-level debugging. Without it, you're limited to assembly-level debugging with raw memory addresses and register dumps. === Why this specification is needed === The 6502 family presents unique challenges that modern DWARF wasn't designed for. Unlike contemporary processors with large, uniform register files, 6502 systems blur the line between registers and memory. Zero page locations function as compiler-allocated pseudo-registers. Memory-mapped I/O registers control video chips, sound processors, and other peripherals. Banking schemes multiply the effective address space. Different retro development tools have historically assigned their own register numbers, creating incompatibility. An emulator might number the accumulator as register 0, while a compiler uses register 3 for the same hardware. This makes it impossible to share debugging information between tools or use multiple debuggers on the same program. === What this specification provides === This specification establishes a standardized numbering scheme for all registers in 6502-family systems. It defines numbers for CPU registers, status flags, compiler-generated imaginary registers, and chipset registers from video controllers to memory mappers. The system prevents numbering conflicts while remaining compatible with existing tools. === When programmers need to know this === You may need to understand this specification if you're developing retro programming tools, implementing DWARF support in emulators, working with compilers like LLVM-MOS that use advanced register allocation, or building debuggers for 6502 systems. Most casual retro programmers can remain unaware of these details—the specification works behind the scenes to ensure their debugging tools are compatible with each other. The full DWARF specification is at [[DWARF specification]]. e69b3f5ac5e54d6aaf7cfc1a4a3b82ac92cab175